8

I have a class InputConfig which contains a List<IncludeExcludeRule>:

public class InputConfig
{
    // The rest of the class omitted 
    private List<IncludeExcludeRule> includeExcludeRules;
    public List<IncludeExcludeRule> IncludeExcludeRules
    {
        get { return includeExcludeRules; }
        set { includeExcludeRules = value; }
    }
}

public class IncludeExcludeRule
{
    // Other members omitted
    private int idx;
    private string function;

    public int Idx
    {
        get { return idx; }
        set { idx = value; }
    }

    public string Function
    {
        get { return function; }
        set { function = value; }
    }
}

Using ...

FileStream fs = new FileStream(path, FileMode.Create);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(InputConfig));
xmlSerializer.Serialize(fs, this);
fs.Close();

... and ...

StreamReader sr = new StreamReader(path);
XmlSerializer reader = new XmlSerializer(typeof(InputConfig));
InputConfig inputConfig = (InputConfig)reader.Deserialize(sr);

It works like a champ! Easy stuff, except that I need to preserve whitespace in the member function when deserializing. The generated XML file demonstrates that the whitespace was preserved when serializing, but it is lost on deserializing.

<IncludeExcludeRules>
  <IncludeExcludeRule>
    <Idx>17</Idx>
    <Name>LIEN</Name>
    <Operation>E =</Operation>
    <Function>  </Function>
  </IncludeExcludeRule>
</IncludeExcludeRules>

The MSDN documentation for XmlAttributeAttribute seems to address this very issue under the header Remarks, yet I don't understand how to put it to use. It provides this example:

// Set this to 'default' or 'preserve'.
[XmlAttribute("space", 
Namespace = "http://www.w3.org/XML/1998/namespace")]
public string Space 

Huh? Set what to 'default' or 'preserve'? I'm sure I'm close, but this just isn't making sense. I have to think there's just a single line XmlAttribute to insert in the class before the member to preserve whitespace on deserialize.

There are many instances of similar questions here and elsewhere, but they all seem to involve the use of XmlReader and XmlDocument, or mucking about with individual nodes and such. I'd like to avoid that depth.

Michael Liu
  • 52,147
  • 13
  • 117
  • 150
CDTWF
  • 83
  • 1
  • 6
  • What happens when you put `[XmlAttribute("space", Namespace = "http://www.w3.org/XML/1998/namespace")]` on your `Function` property? Is the attribute added? – Panagiotis Kanavos Oct 15 '15 at 16:00
  • Adding that, followed by `public string Space ="preserve";` works. I find it preferable to instead use an XmlReader/XmlWriter as described below, as I can then simply use the annotation `[XmlAttribute("xml:space=preserve")]` – CDTWF Oct 15 '15 at 16:52

4 Answers4

9

To preserve all whitespace during XML deserialization, simply create and use an XmlReader:

StreamReader sr = new StreamReader(path);
XmlReader xr = XmlReader.Create(sr);
XmlSerializer reader = new XmlSerializer(typeof(InputConfig));
InputConfig inputConfig = (InputConfig)reader.Deserialize(xr);

Unlike XmlSerializer.Deserialize(XmlReader), XmlSerializer.Deserialize(TextReader) preserves only significant whitespace marked by the xml:space="preserve" attribute.

Michael Liu
  • 52,147
  • 13
  • 117
  • 150
  • 1
    I actually prefer this approach as it makes more sense than adding a string member named `Space`. – CDTWF Oct 15 '15 at 16:48
5

The cryptic documentation means that you need to specify an additional field with the [XmlAttribute("space", Namespace = "http://www.w3.org/XML/1998/namespace")] whose value is default or preserve. XmlAttribute controls the name of the generated attribute for a field or property. The attribute's value is the field's value.

For example, this class:

public class Group
{
   [XmlAttribute (Namespace = "http://www.cpandl.com")]
   public string GroupName;

   [XmlAttribute(DataType = "base64Binary")]
   public Byte [] GroupNumber;

   [XmlAttribute(DataType = "date", AttributeName = "CreationDate")]
   public DateTime Today;

   [XmlAttribute("space", Namespace = "http://www.w3.org/XML/1998/namespace")]
   public string Space ="preserve";
}

Will be serialized to:

<?xml version="1.0" encoding="utf-16"?>
<Group xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       d1p1:GroupName=".NET" 
       GroupNumber="ZDI=" 
       CreationDate="2001-01-10" 
       xml:space="preserve" 
       xmlns:d1p1="http://www.cpandl.com" />
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Thanks, I wondered if the MSDN documentation was telling me to add a string named Space, decided it made no sense. If only their example had been a wee bit better. – CDTWF Oct 15 '15 at 16:37
2

I believe the part you are missing is to add the xml:space="preserve" to the field, e.g.:

<Function xml:space="preserve">   </Function>

For more details, here is the relevant section in the XML Specification

With annotation in the class definition, according to the MSDN blog it should be:

[XmlAttribute("space=preserve")]

but I remember it being

[XmlAttribute("xml:space=preserve")]
Pavel S
  • 68
  • 8
  • that would involve explicitly editing the XML which I want to avoid. The question is, how do I make that happen with an annotation in the class definition. – CDTWF Oct 15 '15 at 15:57
  • The OP is asking how to do this using the XmlAttribute attribute – Panagiotis Kanavos Oct 15 '15 at 15:57
  • @pksimeonov, that looks perfectly reasonable. The result of adding that annotation is that two spaces in the XML now result in the class member being left at null, whereas before it was setting it to an empty string. Quite the opposite of what one would expect. Yes, I really do want those two spaces. – CDTWF Oct 15 '15 at 16:28
2

Michael Liu's answer above worked for me, but with one caveat. I would have commented on his answer, but my "reputation" is not adequate enough.

I found that using XmlReader did not fully fix the issue, and the reason for this is that the .net property in question had the attribute:

XmlText(DataType="normalizedString")

To rectify this I found that adding the additional attribute worked:

[XmlAttribute("xml:space=preserve")]

Obviously, if you have no control over the .net class then you have a problem.

ms10
  • 83
  • 1
  • 8