An attribute and child element of an XML element can have the same name, which may be one reason why .NET forces elements to be elements and attributes to be attributes.[1] I.e. the following is perfectly well-formed XML
<Data PB="foo">
<PB>bar</PB>
</Data>
XmlSerializer
allows you to bind to both the PB
attribute and child element to distinct .NET properties.
In fact, this provides a workaround for your problem: you could create surrogate properties for each attribute that redirect the value of each to the property binding to the corresponding element, like so:
[XmlRoot("Data")]
public class Data
{
[XmlElement("PB")] public string PB { get; set; }
[XmlElement("MP")] public string MP { get; set; }
[XmlElement("Mode")] public string Mode { get; set; }
[XmlAttribute("PB")] public string PBAttribute { get => PB; set => PB = value; }
[XmlAttribute("MP")] public string MPAttribute { get => MP; set => MP = value; }
[XmlAttribute("Mode")] public string ModeAttribute { get => Mode; set => Mode = value; }
// Prevent the elements from being re-serialized.
public bool ShouldSerializePB() => false;
public bool ShouldSerializeMP() => false;
public bool ShouldSerializeMode() => false;
}
[XmlRoot("PInstrument")]
public class PInstrument
{
public string Type { get; set; }
public Data Data { get; set; }
}
[XmlRoot("Response")]
public class Response
{
public PInstrument PInstrument { get; set; }
}
Demo fiddle here.,
This version of <Data>
always re-serializes the values as attributes. If you need to track whether the values were deserialized as attributes or elements and re-serialize accordingly, you could use the {PropertyName}Specified
pattern:
[XmlRoot("Data")]
public class Data
{
[XmlElement("PB")] public string PB { get; set; }
[XmlElement("MP")] public string MP { get; set; }
[XmlElement("Mode")] public string Mode { get; set; }
[XmlAttribute("PB")] public string PBAttribute { get => PB; set => PB = value; }
[XmlAttribute("MP")] public string MPAttribute { get => MP; set => MP = value; }
[XmlAttribute("Mode")] public string ModeAttribute { get => Mode; set => Mode = value; }
[XmlIgnore] public bool PBSpecified { get; set; } = false; // Serialize as attributes unless specified.
[XmlIgnore] public bool MPSpecified { get; set; } = false;
[XmlIgnore] public bool ModeSpecified { get; set; } = false;
[XmlIgnore] public bool PBAttributeSpecified { get => !PBSpecified; set => PBSpecified = !value; }
[XmlIgnore] public bool MPAttributeSpecified { get => !MPSpecified; set => MPSpecified = !value; }
[XmlIgnore] public bool ModeAttributeSpecified { get => !ModeSpecified; set => ModeSpecified = !value; }
}
Demo fiddle #2 here.
[1] Elements and attributes also appear differently in XSD schemas and have different restrictions on the types of values they can contain.