Reputation: 33
I am attempting to update the objects that being deserialized. Taking one object, renaming, using parts and adding a new object that will hold the remaining objects. Is there a way in .NET to deserialize the old XML into the new format?
e.g.
Old Structure:
<objectinfo>
<element1></element1>
<element2></element2>
<element3></element3>
<element4></element4>
</objectinfo>
New Structure:
<objinfo>
<element1></element1>
<element2></element2>
</objinfo>
<newobject>
<element3></element3>
<element4></element4>
</newobject>
Note I am using XmlSerializer
to deserialize.
Upvotes: 3
Views: 124
Reputation: 117086
Assuming you are using XmlSerializer
to deserialize, if your <objectinfo>
is an immediate child of the root XML element, then deserializing to some DTO type identical to old type and mapping to the new object manually or via automapper solves the problem quite easily.
If, however, the objects being modified are nested deeply inside an object hierarchy being deserialized then the DTO strategy isn't as convenient because XmlSerializer
doesn't offer a general surrogate DTO replacement mechanism. One alternative approach in such situations is to manually handle the unknown elements in an XmlSerializer.UnknownElement
event.
To do this in a general way, introduce the following interface and extension methods for XML deserialization:
public interface IUnknownElementHandler
{
void OnUnknownElement(object sender, XmlElementEventArgs e);
}
public static partial class XmlSerializationHelper
{
public static T LoadFromXml<T>(this string xmlString, XmlSerializer serializer = null)
{
serializer = serializer ?? new XmlSerializer(typeof(T)).AddUnknownElementHandler();
using (var reader = new StringReader(xmlString))
return (T)serializer.Deserialize(reader);
}
public static T LoadFromFile<T>(string filename, XmlSerializer serializer = null)
{
serializer = serializer ?? new XmlSerializer(typeof(T)).AddUnknownElementHandler();
using (var reader = new FileStream(filename, FileMode.Open))
return (T)serializer.Deserialize(reader);
}
public static XmlSerializer AddUnknownElementHandler(this XmlSerializer serializer)
{
serializer.UnknownElement += (o, e) =>
{
var handler = e.ObjectBeingDeserialized as IUnknownElementHandler;
if (handler != null)
handler.OnUnknownElement(o, e);
};
return serializer;
}
}
Then, assuming your new data model looks e.g. like this, where Root
is the top-level object and ContainerType
contains the elements being restructured:
[XmlRoot(ElementName = "Root")]
public class Root
{
public ContainerType ContainerType { get; set; }
}
[XmlRoot(ElementName = "ContainerType")]
public partial class ContainerType
{
[XmlElement(ElementName = "objinfo")]
public Objinfo Objinfo { get; set; }
[XmlElement(ElementName = "newobject")]
public Newobject Newobject { get; set; }
}
[XmlRoot(ElementName = "objinfo")]
public class Objinfo
{
[XmlElement(ElementName = "element1")]
public string Element1 { get; set; }
[XmlElement(ElementName = "element2")]
public string Element2 { get; set; }
}
[XmlRoot(ElementName = "newobject")]
public class Newobject
{
[XmlElement(ElementName = "element3")]
public string Element3 { get; set; }
[XmlElement(ElementName = "element4")]
public string Element4 { get; set; }
}
Add an OnUnknownElement
handler to ContainerType
as follows:
public partial class ContainerType : IUnknownElementHandler
{
#region IUnknownElementHandler Members
void IUnknownElementHandler.OnUnknownElement(object sender, XmlElementEventArgs e)
{
var container = (ContainerType)e.ObjectBeingDeserialized;
var element1 = e.Element.SelectSingleNode("element1");
var element2 = e.Element.SelectSingleNode("element2");
if (element1 != null || element2 != null)
{
container.Objinfo = container.Objinfo ?? new Objinfo();
if (element1 != null)
container.Objinfo.Element1 = element1.InnerText;
if (element2 != null)
container.Objinfo.Element2 = element2.InnerText;
}
var element3 = e.Element.SelectSingleNode("element3");
var element4 = e.Element.SelectSingleNode("element4");
if (element3 != null || element4 != null)
{
container.Newobject = container.Newobject ?? new Newobject();
if (element3 != null)
container.Newobject.Element3 = element3.InnerText;
if (element4 != null)
container.Newobject.Element4 = element4.InnerText;
}
}
#endregion
}
Then when you deserialize your Root
from a file using the LoadFromFile
method above:
var root = XmlSerializationHelper.LoadFromFile<Root>(filename);
The obsolete, unknown XML elements will postprocessed by the ContainerType
handler.
Demo fiddle here.
Upvotes: 2