To clarify your question, you want to be able to design your Customer
DTO such that it can be used to deserialize the following simple XML:
<SalesOrder>
<orderType>SO</orderType>
<customer>10000</customer>
</SalesOrder>
As well as the following more complex XML:
<SalesOrder>
<orderType>SO</orderType>
<customer>
<number>10000</number>
<internalId>2462</internalId>
<lastModifiedDateTime>0001-01-01</lastModifiedDateTime>
</customer>
</SalesOrder>
However, the following XML would be invalid and attempting to deserialize it should throw an exception, since it has a both a simple customer number and a complex set of customer values:
<SalesOrder>
<orderType>SO</orderType>
<customer>10000<number>10000</number>
<internalId>2462</internalId>
<lastModifiedDateTime>0001-01-01</lastModifiedDateTime>
</customer>
</SalesOrder>
You also want to be able to choose whether the customer should be re-serialized in simple or complex format.
This can be done with XmlSerializer
, but there is no built-in support for an element type that might have a text value, or might have a sequence of child elements -- but not both. Thus you will need to support this logic in the class itself. The following does this:
[XmlRoot(ElementName = "customer")]
public class Customer
{
bool? isSimpleFormat;
[XmlIgnore]
public bool IsSimpleFormat { get { return isSimpleFormat == true; } set { isSimpleFormat = value; } }
[XmlIgnore]
bool IsSimpleFormatDefined { get { return isSimpleFormat != null; } }
[XmlIgnore]
bool IsComplexPropertySpecified
{
get
{
return !IsSimpleFormat;
}
set
{
if (IsSimpleFormatDefined && IsSimpleFormat)
{
throw new InvalidOperationException("Cannot set a complex property on a simple customer");
}
IsSimpleFormat = !value;
}
}
[XmlElement(ElementName = "number")]
public string Number { get; set; }
[XmlIgnore]
public bool NumberSpecified { get { return IsComplexPropertySpecified; } set { IsComplexPropertySpecified = value; } }
[XmlElement(ElementName = "internalId")]
public string InternalId { get; set; }
[XmlIgnore]
public bool InternalIdSpecified { get { return IsComplexPropertySpecified; } set { IsComplexPropertySpecified = value; } }
[XmlElement(ElementName = "lastModifiedDateTime")]
public string LastModifiedDateTime { get; set; }
[XmlIgnore]
public bool LastModifiedDateTimeSpecified { get { return IsComplexPropertySpecified; } set { IsComplexPropertySpecified = value; } }
[XmlText]
public string SimpleNumber
{
get
{
return IsSimpleFormat ? Number : null;
}
set
{
IsComplexPropertySpecified = false;
Number = value;
}
}
}
[XmlRoot(ElementName = "SalesOrder")]
public class SalesOrder
{
[XmlElement(ElementName = "orderType")]
public string OrderType { get; set; }
[XmlElement(ElementName = "customer")]
public Customer Customer { get; set; }
}
Some notes:
When initially constructed, a Customer
defaults to being in complex format, but will adapt depending whether a child element or text value is encountered during deserialization. Once the format is set, it will no longer adapt and an exception will be thrown if additional nodes are encountered in the wrong format.
At any time after construction, your code can change whether a Customer
is to be serialized in simple or complex format by setting the Boolean property Customer.IsSimpleFormat
.
The type takes advantage of the *Specified
pattern of XmlSerializer
to track and control whether or not the child properties of Customer
were deserialized, and should be serialized.
The XML text value of an element that corresponds to a complex POCO can be mapped to a property by marking that property of that POCO with [XmlText]
.
Sample fiddle.