3

If I have a xml file that looks like

<Foo>
  <Name>Some Data</Name>
  <Bar_Data>Other Data</Bar_Data>
  <Bar_MoreData>More Data</Bar_MoreData>
</Foo>

And I want to turn it in to a C# class that looks like

public class Foo
{
    public string Name {get; set; }
    public Bar Bar { get; set; }
}

public class Bar
{
    public string Data { get; set; }
    public string MoreData { get; set; }
}

Is there any way to accomplish this with just simple data annotations (XmlRoot, XmlElement, etc.) or is my only option to implement IXmlSerializable?

EDIT: Note, I only ever need to deserialize the data. I get the XML from a 3rd party source and I do not need to ever serialize Foo back in to XML (if that makes it easier).

5
  • 1) Just to confirm: you're using XmlSerializer, right? 2) I don't think this can be done entirely with simple data annotations, but there are still easier methods than implementing IXmlSerializable which is quite burdensome. Would those be useful answers? Commented Aug 7, 2015 at 20:11
  • @bdc 1) I am using XmlSerializer right now but I am not tied to it 3) I am open to any solution that does not use a GPL'ed library (I can not use any library that would require me to release my source code, but LGPL or any other OSS that does not require releasing your source is fine) Commented Aug 7, 2015 at 20:19
  • @ScottChamberlain, are any of the options from my answer suitable for your problem or you have other constraints that you did not mention yet? Commented Aug 8, 2015 at 20:25
  • @VitaliyTsvayer Its good and I will likely accept it, I just wanted to give it a few days to not discurage other answres. Commented Aug 8, 2015 at 20:43
  • @ScottChamberlain, indeed here is one more option... Commented Aug 8, 2015 at 22:03

1 Answer 1

4

One option is to use XmlAnyElementAttribute as below:

public class Foo
{
    public string Name { get; set; }
    public Bar Bar { get; set; }

    [XmlAnyElementAttribute]
    public XmlElement[] BarElements
    {
        get { return null; }
        set
        {
            Bar = new Bar();
            var barType = Bar.GetType();
            foreach (var prop in value)
                barType.GetProperty(prop.Name.Substring(4)).SetValue(Bar, prop.InnerText);
        }
    }
}

public class Bar
{
    public string Data { get; set; }
    public string MoreData { get; set; }
}

When XmlSerializer does not recognize an element it adds it to property of type XmlElement[] marked by XmlAnyElementAttribute. That is where you can process Bar properties. I used reflection there to show the idea.

Another option is to deserialize twice and connect Bar with Foo:

public class Foo
{
    public string Name { get; set; }
    public Bar Bar { get; set; }
}

[XmlRoot("Foo")]
public class Bar
{
    [XmlElement("Bar_Data")]
    public string Data { get; set; }
    [XmlElement("Bar_MoreData")]
    public string MoreData { get; set; }
}

var foo = (Foo) new XmlSerializer(typeof (Foo)).Deserialize(...);
var bar = (Bar) new XmlSerializer(typeof (Bar)).Deserialize(...);
foo.Bar = bar

Yet another option with no intrusion to classes being deserialized:

public class Foo
{
    public string Name { get; set; }
    public Bar Bar { get; set; }
}

public class Bar
{
    public string Data { get; set; }
    public string MoreData { get; set; }
}

var fooSerializer = new XmlSerializer(typeof (Foo));
fooSerializer.UnknownElement += (sender, e) =>
{
    var foo = (Foo) e.ObjectBeingDeserialized;
    if(foo.Bar == null)
        foo.Bar = new Bar();
    var propName = e.Element.Name.Substring(4);
    typeof(Bar).GetProperty(propName).SetValue(foo.Bar, e.Element.InnerText);
};

var fooInstance = fooSerializer.Deserialize(...);

and if double deserialization or reflection is problematic performance wise, then you could create a surrogate proxy class:

    [XmlRoot("Foo")]
    public class FooSurrogate
    {
        public string Name { get; set; }
        public string Bar_Data { get; set; }
        public string Bar_MoreData { get; set; }

        public Foo ToFoo()
        {
            return new Foo
            {
                Name = Name,
                Bar = new Bar
                {
                    Data = Bar_Data,
                    MoreData = Bar_MoreData
                }
            };
        }
    }

    var seializer = new XmlSerializer(typeof (FooSurrogate));
    var foo = ((FooSurrogate) seializer.Deserialize(...)).ToFoo();
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.