Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preserve whitespace-only element content when deserializing XML using XmlSerializer

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.

like image 924
CDTWF Avatar asked Oct 15 '15 15:10

CDTWF


People also ask

Can I make XmlSerializer ignore the namespace on Deserialization?

Yes, you can tell the XmlSerializer to ignore namespaces during de-serialization.

What is XmlSerializer in java?

public XMLSerializer(java.io.Writer writer, OutputFormat format) Constructs a new serializer that writes to the specified writer using the specified output format. If format is null, will use a default output format.

What is serializing and deserializing XML?

Serialization is the process of converting an object into a form that can be readily transported. For example, you can serialize an object and transport it over the Internet using HTTP between a client and a server. On the other end, deserialization reconstructs the object from the stream.

Why do we use XmlSerializer class?

XmlSerializer enables you to control how objects are encoded into XML. The XmlSerializer enables you to control how objects are encoded into XML, it has a number of constructors.


4 Answers

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.

like image 130
Michael Liu Avatar answered Oct 12 '22 19:10

Michael Liu


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" />
like image 25
Panagiotis Kanavos Avatar answered Oct 12 '22 21:10

Panagiotis Kanavos


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")]
like image 41
Pavel S Avatar answered Oct 12 '22 20:10

Pavel S


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.

like image 39
ms10 Avatar answered Oct 12 '22 20:10

ms10