yonan2236
yonan2236

Reputation: 13659

XML changes after Serialization

I noticed that my xml changes after serialization. e.g.
* the ns in the start of the Message element disappears
* xmlns:ns attribute becomes xmlns
* there are new attributes added in Message element - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" and xmlns:xsd="http://www.w3.org/2001/XMLSchema"
* new attribute in Header element - xmlns

How can I keep the original form of the xml and prevent these attributes from being added?

Here's how the original XML looks like:

<?xml version="1.0" encoding="UTF-8"?>
<ns:Message xmlns:ns="http://example.com">
  <Header version="1.0">
    <Sender>3015207400109</Sender>
    <Receiver>8711200999903</Receiver>
    <MessageID>000D2613F64AC021ED783C084735EC78E53</MessageID>
    <CreationDateTime>2017-03-21T08:00:47Z</CreationDateTime>
  </Header>
</ns:Message>

And here's the xml after it has been serialized:

<?xml version="1.0" encoding="UTF-8"?>
<Message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://example.com">
  <Header version="1.0" xmlns="">
    <Sender>3015207400109</Sender>
    <Receiver>8711200999903</Receiver>
    <MessageID>000D2613F64AC021ED783C084735EC78E53</MessageID>
    <CreationDateTime>2017-03-21T08:00:47Z</CreationDateTime>
  </Header>
</Message>

The code below is the (generated) class that represents the Message xml.

[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://example.com")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://example.com", IsNullable = false)]
public partial class Message
{

    private Header headerField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Namespace = "")]
    public Header Header
    {
        get
        {
            return this.headerField;
        }
        set
        {
            this.headerField = value;
        }
    }
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Header
{

    private ulong senderField;

    private ulong receiverField;

    private string messageIDField;

    private System.DateTime creationDateTimeField;

    private decimal versionField;

    /// <remarks/>
    public ulong Sender
    {
        get
        {
            return this.senderField;
        }
        set
        {
            this.senderField = value;
        }
    }

    /// <remarks/>
    public ulong Receiver
    {
        get
        {
            return this.receiverField;
        }
        set
        {
            this.receiverField = value;
        }
    }

    /// <remarks/>
    public string MessageID
    {
        get
        {
            return this.messageIDField;
        }
        set
        {
            this.messageIDField = value;
        }
    }

    /// <remarks/>
    public System.DateTime CreationDateTime
    {
        get
        {
            return this.creationDateTimeField;
        }
        set
        {
            this.creationDateTimeField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public decimal version
    {
        get
        {
            return this.versionField;
        }
        set
        {
            this.versionField = value;
        }
    }
}

Upvotes: 0

Views: 90

Answers (1)

dbc
dbc

Reputation: 117190

The two XML files you show are semantically identical. Thus, I'd recommend not worrying about the fact that XmlSerializer inserts XML standard namespaces or chooses a different prefixing scheme than was used in the original file.

If, for whatever reason, you must suppress output of the standard namespaces and must preserve the prefixing scheme of the original file, here's what you can do.

Firstly, to omit the xsi and xsd namespaces at the root level, follow the instructions from Omitting all xsi and xsd namespaces when serializing an object in .NET?:

var s = new XmlSerializer(objectToSerialize.GetType());
var  ns = new XmlSerializerNamespaces();
ns.Add("","");
s.Serialize(xmlWriter, objectToSerialize, ns);

Next, in order to keep the original form of the xml you must first somehow capture the actual XML namespaces and prefixes encountered while reading the file and save them in the Message class somewhere for reuse later. Luckily XmlSerializer does support this: you can add an XmlSerializerNamespaces valued public property or field to Message and mark it with [XmlNamespaceDeclarations]. This member will now capture namespaces encountered during deserialization, and cause those namespaces to be added back during serialization.

Putting these two ideas together, you can modify your Message type as follows:

public partial class Message
{
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces XmlFileNamespaces { get; set; }

    /// <summary>
    /// returns a XmlSerializerNamespaces to use when serializing a Message as the root XML object.
    /// If Message was previously deserialized from XML, the actual namespaces observed will be returned.
    /// Otherwise, a default will be returned that suppresses output of the xmlns:xsi and xmlns:xsd namespace attributes.
    /// </summary>
    [XmlIgnore]
    public XmlSerializerNamespaces XmlRootNamespaces
    {
        get
        {
            if (XmlFileNamespaces != null)
                return XmlFileNamespaces;
            var xmlNamespaces = new XmlSerializerNamespaces();
            xmlNamespaces.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
            // xmlNamespaces.Add("ns", "http://example.com"); // Or, if you prefer, add this namespace as well as disabling xmlns:xsi and xmlns:xsd.
            return xmlNamespaces;
        }
    }
}

And serialize from and to XML as follows:

var message = xml.LoadFromXml<Message>();

var reserializedXml = message.GetXml(message.XmlRootNamespaces);

Using the following extension methods:

public static class XmlSerializationHelper
{
    public static T LoadFromXml<T>(this string xmlString)
    {
        using (StringReader reader = new StringReader(xmlString))
        {
            return (T)new XmlSerializer(typeof(T)).Deserialize(reader);
        }
    }

    public static string GetXml<T>(this T obj, XmlSerializerNamespaces ns)
    {
        using (var textWriter = new Utf8StringWriter())
        {
            var settings = new XmlWriterSettings() { Indent = true }; // For cosmetic purposes.
            using (var xmlWriter = XmlWriter.Create(textWriter, settings))
                new XmlSerializer(obj.GetType()).Serialize(xmlWriter, obj, ns);
            return textWriter.ToString();
        }
    }
}

// http://stackoverflow.com/questions/3862063/serializing-an-object-as-utf-8-xml-in-net
public class Utf8StringWriter : StringWriter
{
    public override Encoding Encoding
    {
        get { return Encoding.UTF8; }
    }
}

Prototype fiddle.

Upvotes: 1

Related Questions