user2092583
user2092583

Reputation: 1

deserialize xml to object

I have two identical XML files containing info about persons.they differ only in their tag names. for e.g. for the first, i have

<name>
<surname>
<address>... 

and in the second one, i have

<name_person>
<surname_person>
<address_person>

i need to "de-serialize" them into two separate objects in vb.net. i have only one class with properties Name,Surname,Address.. is it possible to use just this one class for the second XML file too?because the latter's tag names don't match.

thanks

Upvotes: 0

Views: 3004

Answers (3)

Ted
Ted

Reputation: 3260

The easiest thing, I think would be to control the deserialization separately. I looked for finding aliases for the XmlElement attribute, but I didn't see anything like that. Furthermore, when you serialize the objects again, how would it know which tags it is supposed to use.

Once you have deserialized from your second object, it would not be that hard in code to create new instances of your first class and copy the properties over.

You can also do something like this, which makes one class that is great at reading, but you shouldn't use this class for writing or it will write all the properties.

VB.NET

<XmlRoot("PersonList")> _
Public Class PersonList
    <XmlElement("person")> _
    Public Property People() As Person()
        Get
            Return m_People
        End Get
        Set
            m_People = Value
        End Set
    End Property
    Private m_People As Person()
End Class

Public Class Person
    Private _name As String

    <XmlElement("name")> _
    Public Property Name() As String
        Get
            Return _name
        End Get
        Set
            _name = value
        End Set
    End Property

    <XmlElement("name_person")> _
    Public Property NamePerson() As String
        Get
            Return _name
        End Get
        Set
            _name = value
        End Set
    End Property

End Class

C#

[XmlRoot("PersonList")]
public class PersonList {
    [XmlElement("person")]
    public Person[] People { get; set; }
}

public class Person {
    private String _name;

    [XmlElement("name")]
    public String Name {get{return _name;} set{_name = value;}}

    [XmlElement("name_person")]
    public String NamePerson {get{return _name;} set{_name = value;}}

}

reference: XML deserialize: different xml schema maps to the same C# class

Alternately, it looks like you can use your original class, but then handle the XmlSerializer.UnknownElement event.

(untested) VB.Net

Private Shared Sub serializer_UnknownElement(sender As Object, e As XmlElementEventArgs)
    Dim target = DirectCast(e.ObjectBeingDeserialized, Person)
    If e.Element.Name = "name_person" Then
        target.Name = e.Element.InnerText
    End If
End Sub

C#

static void serializer_UnknownElement(object sender, XmlElementEventArgs e)
{
    var target = (Person) e.ObjectBeingDeserialized;
    if( e.Element.Name == "name_person")
    {
        target.Name = e.Element.InnerText;
    }
}

But again, this won't let you save back to the old format, only load from your old format into your new class.

reference: http://weblogs.asp.net/psteele/archive/2011/01/31/xml-serialization-and-the-obsolete-attribute.aspx

reference: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.unknownelement.aspx

Upvotes: 1

Nathan Dace
Nathan Dace

Reputation: 1555

You could use XSLT to update the second document to match the element names you're expecting. That way, you don't have to handle the different element names in the code.

Transform the second document using XSLT, something like:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="//name_person">
    <name>
     <xsl:apply-templates select="@*|node()" />
    </name>
  </xsl:template>
  <xsl:template match="//surname_person">
    <surname>
      <xsl:apply-templates select="@*|node()" />
    </surname>
  </xsl:template>
  <xsl:template match="//address_person">
    <address>
      <xsl:apply-templates select="@*|node()" />
    </address>
  </xsl:template>
</xsl:stylesheet>

It apply this programatically to your document, in C#, you could do:

XslCompiledTransformtransform = new XslCompiledTransform(); 
transorm.Load("update.xsl"); 
transorm.Transform("source.xml","new.xml"); 

In VB:

Dim transform As XslTransform
transform = New XslTransform()
transform .Load("update.xsl")
transform .Transform("source.xml", "new.xml")

The result will then be the element names will match across both documents, and you could then run your deserializer on the XML without having to worry about handling mismatched element names.

Upvotes: 0

You should be able to use a SerializationBinder where you can specify (remap) the type of the element being deserialized.

Upvotes: 0

Related Questions