Tutorial :XML serialize annotations



Question:

I have a situation where I have an xml file that I don't want to modify. The AddAnnotation function in XElement class provides an option to add memory-only data which is not serialized and not part of the XML.

I want to be able to save these annotations (for example: to another xml file) and then to deserialize both the xml and the annotations in order to get the same object I had.

I don't want to change the original xml and that's the reason that I use annotations.

To summarize, I want to be able to add custom data to an xml file. This data won't be a part of the xml when I serialize it or it will be a part of the xml but I would be able to retrieve the original xml easily.

Do you have any recommendation how I can do such a thing?

Edit: Should I use xml processing instructions? Are processing instructions intended for this kind of usage?


Solution:1

It sounds to me like the simplest approach would be to use regular nodes, but in a different xml namespace - i.e.

<foo standardAttrubute="abc" myData:customAttribute="def">      <standardElement>ghi</standardElement >      <myData:customElement>jkl</myData:customElement>  </foo>  

(where myData is an xmlns alias for the namespace-uri)

In many cases, readers are only checking for data in their namespace (or the default/blank namespace) - values in custom namespaces are generally skipped.

To get pack the original xml, one simple approach would be to run it through an xslt that only respects the default/original namespace.


XNamespace myData = XNamespace.Get("http://mycustomdata/");  XElement el = new XElement("foo",      new XAttribute(XNamespace.Xmlns + "myData", myData.NamespaceName),      new XAttribute("standardAttribute", "abc"),      new XAttribute(myData + "customAttribute", "def"),      new XElement("standardElement", "ghi"),      new XElement(myData + "customAttribute", "jkl"));  string s = el.ToString();  

To remove such data from an XElement, perhaps:

    static void Strip(XElement el, XNamespace ns) {          List<XElement> remove = new List<XElement>();          foreach (XElement child in el.Elements()) {              if (child.Name.Namespace == ns) {                  remove.Add(child);              } else {                  Strip(child, ns);              }          }          remove.ForEach(child => child.Remove());            foreach (XAttribute child in              (from a in el.Attributes()               where a.Name.Namespace == ns               select a).ToList()) {              child.Remove();          }      }  


Solution:2

The original question used the word "Serialize" but then also mentioned XElement and annotation. To me these are two different things.

If you really want to use the XmlSerializer:
What I would do is use XmlAttributeOverrides, to differentiate the serialization. With the XmlAttributeOverrides you can programmatically, at runtime, override the xml serialization attributes that decorate your types.

Within your type, you can have a field/property that is intended to hold the annotation/documentation. Decorate that with XmlIgnore. Then, create one instance of the XmlSerializer that accepts no overrides. The annotation will not be serialized or de-serialized. Create another instance of the XmlSerializer for that type, using an XmlAttributeOverrides object. Specify an override for the XmlIgnore'd property (use XmlElementAttribute), as well as overrides for any attributes on any of the other members (use XmlIgnore=true).

Serialize the instance twice, one with each serializer.


Edit: here's the code:

public class DTO  {      [XmlIgnore]      public string additionalInformation;        [XmlElement(Order=1)]      public DateTime stamp;        [XmlElement(Order=2)]      public string name;        [XmlElement(Order=3)]      public double value;        [XmlElement(Order=4)]      public int index;  }        public class OverridesDemo  {       public void Run()      {          DTO dto = new DTO              {                  additionalInformation = "This will bbe serialized separately",                  stamp = DateTime.UtcNow,                  name = "Marley",                  value = 72.34,                  index = 7              };              // ---------------------------------------------------------------          // 1. serialize normally          // this will allow us to omit the xmlns:xsi namespace          var ns = new XmlSerializerNamespaces();          ns.Add( "", "" );            XmlSerializer s1 = new XmlSerializer(typeof(DTO));            var builder = new System.Text.StringBuilder();          var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };            Console.WriteLine("\nSerialize using the in-line attributes: ");          using ( XmlWriter writer = XmlWriter.Create(builder, settings))          {              s1.Serialize(writer, dto, ns);          }          Console.WriteLine("{0}",builder.ToString());          Console.WriteLine("\n");                      // ---------------------------------------------------------------            // ---------------------------------------------------------------          // 2. serialize with attribute overrides          // use a non-empty default namespace          ns = new XmlSerializerNamespaces();          string myns = "urn:www.example.org";          ns.Add( "", myns);            XmlAttributeOverrides overrides = new XmlAttributeOverrides();            XmlAttributes attrs = new XmlAttributes();          // override the (implicit) XmlRoot attribute          XmlRootAttribute attr1 = new XmlRootAttribute              {                  Namespace = myns,                  ElementName = "DTO-Annotations",              };          attrs.XmlRoot = attr1;            overrides.Add(typeof(DTO), attrs);          // "un-ignore" the first property          // define an XmlElement attribute, for a type of "String", with no namespace          var a2 = new XmlElementAttribute(typeof(String)) { ElementName="note", Namespace = myns };            // add that XmlElement attribute to the 2nd bunch of attributes          attrs = new XmlAttributes();          attrs.XmlElements.Add(a2);          attrs.XmlIgnore = false;             // add that bunch of attributes to the container for the type, and          // specifically apply that bunch to the "additionalInformation" property           // on the type.          overrides.Add(typeof(DTO), "additionalInformation", attrs);            // now, XmlIgnore all the other properties          attrs = new XmlAttributes();          attrs.XmlIgnore = true;                 overrides.Add(typeof(DTO), "stamp", attrs);          overrides.Add(typeof(DTO), "name",  attrs);          overrides.Add(typeof(DTO), "value", attrs);          overrides.Add(typeof(DTO), "index", attrs);            // create a serializer using those xml attribute overrides          XmlSerializer s2 = new XmlSerializer(typeof(DTO), overrides);            Console.WriteLine("\nSerialize using the override attributes: ");          builder.Length = 0;          using ( XmlWriter writer = XmlWriter.Create(builder, settings))          {              s2.Serialize(writer, dto, ns);          }          Console.WriteLine("{0}",builder.ToString());          Console.WriteLine("\n");                      // ---------------------------------------------------------------      }  }  

output, using the in-line attributes:

<DTO>    <stamp>2009-06-30T02:17:35.918Z</stamp>    <name>Marley</name>    <value>72.34</value>    <index>7</index>  </DTO>  

output, using the override attributes:

<DTO-Annotations xmlns="urn:www.example.org">    <note>This will bbe serialized separately</note>  </DTO-Annotations>  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »