问题
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.
回答1:
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 anXmlChoiceIdentifierAttribute
in addition, and adds extra logic for making the choice.
Thus, you have the following options to deserialize your XML:
Subclass your
Parameter
class and specify different types for each element name, using [XmlElementAttribute(String, Type)]. The specificParameter
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; } }
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 theParameters
array, and theenum
values therein will indicate the XML element name actually encountered for each parameter.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.
回答2:
What if you do something like this:
//get the xml doc
const string str = @"<root>
<Parameter1>3</Parameter1>
<Parameter2>4</Parameter2>
</root>";
var xml = new XmlDocument();
//load it
xml.LoadXml(str);
//get the nodes where the names contain the string parameter
var xnList = xml.SelectNodes("//*[contains(name(),'Parameter')]");
//create a list of parameters
var list = new List<Parameter>();
//populate the list with the value in the node's innertext
foreach (XmlNode xn in xnList)
{
list.Add(new Parameter{ Value = int.Parse(xn.InnerText) } );
}
foreach(var param in list)
Console.WriteLine(param.Value); //should print 3 and 4
I am using this class as an example:
class Parameter{
public int Value { get; set; }
}
来源:https://stackoverflow.com/questions/37307203/xml-deserialization-merging-two-elements-into-a-single-listt-object