Deserializing XML in C# where a property could be

2019-09-15 22:51发布

I'm writing a C# library that, as one of its functions, needs to be able to accept XML of the following forms from a web service and deserialize them.

Form 1:

<results>
  <sample>
    <status>status message</status>
    <name>sample name</name>
    <igsn>unique identifier for this sample</igsn>
  </sample>
</results>

Form 2:

<results>
  <sample name="sample name">
    <valid code="InvalidSample">no</valid>
    <status>Not Saved</status>
    <error>error message</error> 
  </sample>
</results>

Here's my class that I'm deserializing to:

namespace MyNamespace
{
    [XmlRoot(ElementName = "results")]
    public class SampleSubmissionResponse
    {

        [XmlElement("sample")]
        public List<SampleSubmissionSampleResultRecord> SampleList { get; set; }

       ...
    }


    public class SampleSubmissionSampleResultRecord
    {
     ...

        /* RELEVANT PROPERTY RIGHT HERE */
        [XmlAttribute(Attribute = "name")]
        [XmlElement(ElementName = "name")]
        public string Name { get; set; }

      ...
    }

    public class SampleSubmissionValidRecord
    {
       ...
    }
}

The problem is that in one XML sample, the name attribute of the Sample element is an element, and in the other it's an attribute. If I decorate the property of my class with both XmlAttribute and XmlElement, I get an exception thrown when creating an instance of XmlSerializer.

I've been googling for a good while now, and I can't find any docs that deal with this situation. I assume, but don't know for sure, that this is because when creating an XML schema, you're not supposed to use the same name for an attribute and a child element of the same element.

So, what do I do here?

One solution might be to have two totally separate models for the different types. That would probably work, but doesn't seem very elegant.

Another option might be to implement IXmlSerializable and write some elaborate code to handle this in the deserialize method. That would be an awfully verbose solution to a simple problem.

Third option I'm hoping for: some way of applying both XmlAttribute and XmlElement to the same property, or an equivalent "either-or" attribute.

Fourth option: Change the web service the XML comes from to use one form consistently. Unfortunately, the folks who own it may not be willing to do this.

1条回答
爷的心禁止访问
2楼-- · 2019-09-15 23:31

Specify only one attribute to Name property. This will correctly parse out the first xml form.

public class SampleSubmissionSampleResultRecord
{
    [XmlElement(ElementName = "name")]
    public string Name { get; set; }
}

To parse the second xml form, subscribe the XmlSerializer to the UnknownAttribute event.

var xs = new XmlSerializer(typeof(SampleSubmissionResponse));
xs.UnknownAttribute += Xs_UnknownAttribute;

In the event handler, we get the desired value.

private void Xs_UnknownAttribute(object sender, XmlAttributeEventArgs e)
{
    var record = (SampleSubmissionSampleResultRecord)e.ObjectBeingDeserialized;
    record.Name = e.Attr.Value;
}
查看更多
登录 后发表回答