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.
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 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; }
}
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With