I want to serialise XML documents from a third-party service that either come in as any of the two formats (I added indenting for easier readability):
1.
<STADMessage>Invalid Request, no content provided!</STADMessage>
2.
<STADMessage>
<Message>Invalid Request, see log for detail using reference: ASDFL210359872305982035</Message>
</STADMessage>
Right now I'm hacking the XML document before it gets serialised with the following code
xmlDocument.Replace("<STADMessage><Message>", "<STADMessages><Message>")
.Replace("</Message></STADMessage>", "</Message></STADMessages>");
Snippet of the serialised class
[XmlElement(ElementName = "STADMessage", IsNullable = true)]
public string STADMessage { get; set; }
[XmlArray(ElementName = "STADMessages", IsNullable = true)]
[XmlArrayItem("Message", typeof(string))]
public List<string> STADMessages { get; set; }
Is there a cleaner way?
If you can get them to change it to a proper structure as @FrankerZ recommends, that would be ideal. If you can't, I hope this helps.
You can account for the variation using a custom serialization object that deserializes differently based on the incoming node type.
Change your STADMessage property's type to the custom type (I'll call it STADMessage
for the heck of it):
[XmlElement(ElementName = "STADMessage", IsNullable = true)]
public STADMessage STADMessage { get; set; }
And here's the STADMessage
class:
public class MySTADMessage : IXmlSerializable
{
public string Message { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
// IsNullable = true is ignored, apparently. You won't get an actual
// null for properties deserialized this way because the serializer
// already created an instance of this class.
if (reader.GetAttribute("nil", XmlSchema.InstanceNamespace) == "true")
return;
reader.ReadStartElement();
while (reader.NodeType == XmlNodeType.Whitespace)
reader.Read();
if (reader.NodeType == XmlNodeType.Text)
{
Message = reader.ReadContentAsString();
}
else if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name != "Message")
throw new Exception("Unexpected element name.");
reader.ReadStartElement();
if (reader.NodeType == XmlNodeType.Text)
{
Message = reader.ReadContentAsString();
}
else
{
throw new Exception("Unexpected node type.");
}
reader.ReadEndElement();
}
else
{
throw new Exception("Unexpected node type.");
}
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
// Not having the extra Message element is simpler.
writer.WriteString(Message);
}
}
It's crude, it doesn't quite implement IXmlSerializable
correctly by today's standards, and it probably doesn't account for everything, but it should get you started.