Reputation: 43
I've come across an annoying problem where I am trying to deserialise an XML payload for one of my controllers, but the payload could either have no namespacing or have namespace prefixes on all elements.
I have tried adding XML attributes to my model with Namespace = ""
in an attempt to have the model not care, but this did not work. I have also tried setting the default namespace on my XmlSerialiser to ""
but this didn't help. I also tried the [XmlNamespaceDeclarations]
attribute and had no luck.
Below is an example of the the two different payloads I could receive, in case I wasn't clear in my description.
<ex:root namespace:ex="http://example.com/ns">
<ex:element>Example</ex:element>
<ex:secondElement>Example2</ex:secondElement>
</ex:root>
<root>
<element>Example</element>
<secondElement>Example</secondElement>
</root>
And below is the model for that payload that I would be trying to deserialise to
[XmlRoot("root")]
public class Root
{
[XmlElement("element")]
public string Element {get; set;}
[XmlElement("secondElement")]
public string SecondElement {get; set;}
}
I sincerely apologise if this has been answered somewhere else but I have tried the solutions to every question I could find relevant to my issue and had no success.
Thank you in advance.
EDIT: Fixed namespaced XML example
Upvotes: 1
Views: 897
Reputation: 239824
Since there are two different possible elements that may appear first inside the root, we need to express that choice (and similarly for the second element). I played around with trying to write the correct code directly but failed, so instead I first wrote a schema to express that was wanted and then used the xsd
command line tool to generate the C# code1.
First, the main schema:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:ex="http://example.com/ns" attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="http://example.com/ns" />
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:choice>
<xs:element ref="ex:element" />
<xs:element name="element" >
</xs:element>
</xs:choice>
<xs:choice>
<xs:element ref="ex:secondElement" />
<xs:element name="secondElement" />
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
And then we need another schema to describe the ns
schema:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="XMLSchema1"
targetNamespace="http://example.com/ns"
elementFormDefault="qualified"
xmlns="http://example.com/ns"
xmlns:mstns="http://example.com/ns"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:element name="element"/>
<xs:element name="secondElement"/>
</xs:schema>
And then we run xsd /classes XmlFile1.xsd XMLSchema1.xsd
(assuming that's the names given to the two xsd
files.
This spits out a class:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System.Xml.Serialization;
//
// This source code was auto-generated by xsd, Version=4.6.1055.0.
//
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
public partial class root {
private object itemField;
private ItemChoiceType itemElementNameField;
private object item1Field;
private Item1ChoiceType item1ElementNameField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("element", typeof(object))]
[System.Xml.Serialization.XmlElementAttribute("element", typeof(object), Namespace="http://example.com/ns")]
[System.Xml.Serialization.XmlChoiceIdentifierAttribute("ItemElementName")]
public object Item {
get {
return this.itemField;
}
set {
this.itemField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public ItemChoiceType ItemElementName {
get {
return this.itemElementNameField;
}
set {
this.itemElementNameField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("secondElement", typeof(object), Namespace="http://example.com/ns")]
[System.Xml.Serialization.XmlElementAttribute("secondElement", typeof(object))]
[System.Xml.Serialization.XmlChoiceIdentifierAttribute("Item1ElementName")]
public object Item1 {
get {
return this.item1Field;
}
set {
this.item1Field = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public Item1ChoiceType Item1ElementName {
get {
return this.item1ElementNameField;
}
set {
this.item1ElementNameField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(IncludeInSchema=false)]
public enum ItemChoiceType {
/// <remarks/>
element,
/// <remarks/>
[System.Xml.Serialization.XmlEnumAttribute("http://example.com/ns:element")]
element1,
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(IncludeInSchema=false)]
public enum Item1ChoiceType {
/// <remarks/>
[System.Xml.Serialization.XmlEnumAttribute("http://example.com/ns:secondElement")]
secondElement,
/// <remarks/>
[System.Xml.Serialization.XmlEnumAttribute("secondElement")]
secondElement1,
}
Which you can probably edit/adjust to fit your exact needs. E.g. I don't think the types used here (object
) are exactly ideal, and can probably be adjusted to string
.
This is what I simplified it to:
using System.Xml.Serialization;
[System.Serializable()]
[XmlType(AnonymousType = true)]
[XmlRoot("root", Namespace = "", IsNullable = false)]
public partial class Root
{
[XmlElement("element", typeof(string))]
[XmlElement("element", typeof(string), Namespace = "http://example.com/ns")]
[XmlChoiceIdentifier("ElementType")]
public string Element { get; set; }
[XmlIgnore()]
public ElementType ElementType { get; set; }
[XmlElement("secondElement", typeof(string), Namespace = "http://example.com/ns")]
[XmlElement("secondElement", typeof(string))]
[XmlChoiceIdentifier("SecondElementType")]
public string SecondElement { get; set; }
[XmlIgnore()]
public SecondElementType SecondElementType { get; set; }
}
/// <remarks/>
[System.Serializable()]
[XmlType(IncludeInSchema = false)]
public enum ElementType
{
element,
[XmlEnum("http://example.com/ns:element")]
nsElement,
}
/// <remarks/>
[System.Serializable()]
[XmlType(IncludeInSchema = false)]
public enum SecondElementType
{
secondElement,
[XmlEnum("http://example.com/ns:secondElement")]
nsSecondElement,
}
1If you have an actual supplied xsd
to describe the XML then I'd suggest using that rather than writing new ones from scratch. The one's I've supplied are practically the minimum to be able to run xsd
successfully. Somehow I doubt it though since this looks more the XML is being produced by someone not really understanding XML, namespaces, schemas, etc.
Upvotes: 0
Reputation: 2073
Not sure how have you tried to deserialize, but something like this will work:
var xml = File.ReadAllText("sample.xml");
var serializer = new XmlSerializer(typeof(Root));
var obj = serializer.Deserialize(new StringReader(xml));
Upvotes: 1