Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.6k views
in Technique[技术] by (71.8m points)

c# - Xml Deserialization - Merging two elements into a single List<T> object

I have an XML document, and using deserialization, is there a way to combine two elements into one object?

XML example:

<Parameter1>3</Parameter1>
<Parameter2>4</Parameter2>

I want to create a list (of type Parameter) that contains both items, 3 and 4.

I've tried using XmlArrayItem such as:

[XmlArrayItem("Parameter1")]
[XmlArrayItem("Parameter2")]
[XmlArray]
public Parameter[] Parameters; // have also tried this as public List<Parameter> Parameters = new List<Parameter>();

I've tried using XmlElements (but I can't figure out how to combine them):

[XmlElement("Parameter1")]
public List<Parameter> Parameters = new List<Parameter>();

Is there any way to do this without just creating two separate lists and combining them at a later point?

Please note that changing the XML format is not an option.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Your XML has a schema that includes a choice element. A choice element indicates that one of a fixed set of elements -- <Parameter1> and <Parameter2> in your case -- will occur in the XML. XmlSerializer supports choice elements as is explained in Choice Element Binding Support:

If individual choice elements' types differ along with their names, Xsd.exe applies only XmlElementAttribute attributes to a public member. If they differ only by name, Xsd.exe applies an XmlChoiceIdentifierAttribute in addition, and adds extra logic for making the choice.

Thus, you have the following options to deserialize your XML:

  1. Subclass your Parameter class and specify different types for each element name, using [XmlElementAttribute(String,?Type)]. The specific Parameter subclass instantiated would thereby capture the XML element name.

    I.e. you could do:

    public abstract class Parameter
    {
        [XmlText]
        public string Value { get; set; } // Could be int if you prefer.
    }
    
    public class Parameter1 : Parameter
    {
    }
    
    public class Parameter2 : Parameter
    {
    }
    
    [XmlType("Root")]
    public class RootObject
    {
        [XmlElement("Parameter1", typeof(Parameter1))]
        [XmlElement("Parameter2", typeof(Parameter2))]
        public Parameter[] Parameters { get; set; }
    }
    
  2. If you want to use the same Parameter type to deserialize both <Parameter1> and <Parameter2> elements, you must introduce an ancillary XmlChoiceIdentifierAttribute array to capture the XML element name:

    public class Parameter
    {
        [XmlText]
        public string Value { get; set; }
    }
    
    [XmlType("Root")]
    public class RootObject
    {
        [XmlElement("Parameter1", typeof(Parameter))]
        [XmlElement("Parameter2", typeof(Parameter))]
        [XmlChoiceIdentifier("ParametersElementName")]
        public Parameter[] Parameters { get; set; }
    
        [XmlIgnore]
        public ParametersChoiceType[] ParametersElementName { get; set; }
    }
    
    [XmlType(IncludeInSchema = false)]
    public enum ParametersChoiceType
    {
        Parameter1,
        Parameter2,
    }
    

    After deserialization, the ParametersElementName array will have the same number of entries as the Parameters array, and the enum values therein will indicate the XML element name actually encountered for each parameter.

  3. As a variation of option 2, if you do not need to capture the XML element name and just want to deserialize the values, you could create a "fake" choice array property as follows:

    [XmlType("Root")]
    public class RootObject
    {
        [XmlElement("Parameter1", typeof(Parameter))]
        [XmlElement("Parameter2", typeof(Parameter))]
        [XmlChoiceIdentifier("ParametersElementName")]
        public Parameter[] Parameters { get; set; }
    
        [XmlIgnore]
        public ParametersChoiceType[] ParametersElementName
        {
            get
            {
                if (Parameters == null)
                    return null;
                return Parameters.Select(p => ParametersChoiceType.Parameter1).ToArray();// Arbitrarily return ItemsChoiceType.Parameter1
            }
            set
            {
                // Do nothing - don't care.
            }
        }
    }
    

XmlSerializer requires you to use one of these two options. If it cannot determine a correct element name by type or by item choice identifier, it will throw an InvalidOperationException with the message:

You need to add XmlChoiceIdentifierAttribute to the 'Parameters' member.

Prototype fiddle showing each option.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...