c# XmlSerializer deserializer missing default namespace - c#

Below is an example payload response from an integration I am currently working on. The response does not set a default namespace (xml2 variable in example) and the issue is that XmlSerializer does not assume the default namespace is "d". I have tried setting the default namespace in the XMLSerializer constructor but that doesn't work either. As well, I can't expect the "service" to update/fix their side. Is there some other settings I can pass that will correctly populate the class?
Thanks,
Chuck
using System;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace TestXmlNamespace
{
[XmlRoot(ElementName = "root", Namespace = "my_test_ns1")]
public class Test
{
public string name { get; set; }
public int age { get; set; }
[XmlElement(Namespace = "my_test_ns2")]
public int ageInMonths { get; set; }
public override bool Equals(object obj)
{
return obj is Test b && name == b.name && age == b.age && ageInMonths == b.ageInMonths;
}
public void Run(string str, string name)
{
XmlSerializer serializer = new XmlSerializer(typeof(Test));
using (StringReader rStream = new StringReader(str))
{
Test test = serializer.Deserialize(rStream) as Test;
Console.Out.WriteLine(test.Equals(this) ? $"{name} equals expected" : $"{name} does not equal expected");
}
}
}
class Program
{
const string xml1 = #"<?xml version=""1.0"" encoding=""UTF-8"" ?>
<d:root xmlns:d=""my_test_ns1"" xmlns:v=""my_test_ns2"">
<d:name>Bill</d:name>
<d:age>32</d:age>
<v:ageInMonths>384</v:ageInMonths>
</d:root>
";
const string xml2 = #"<?xml version=""1.0"" encoding=""UTF-8"" ?>
<d:root xmlns:d=""my_test_ns1"" xmlns:v=""my_test_ns2"">
<name>Bill</name>
<age>32</age>
<v:ageInMonths>384</v:ageInMonths>
</d:root>
";
static void Main(string[] args)
{
Test expected = new Test()
{
name = "Bill",
age = 32,
ageInMonths = 384
};
expected.Run(xml1, "xml1");
expected.Run(xml2, "xml2");
}
}
}

I found the solution and I am posting it for those in the future who may run across this problem and hopefully I will help them save some time.
Simply put, when an element does not have a namespace prefix XmlSerializer treats it has not part of a name space so specifying an empty name space in the element attribute handles this. See the updated example code below.
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace TestXmlNamespace
{
public class CustomXmlTextReader : XmlTextReader
{
private readonly string DefaultNamespace = String.Empty;
public CustomXmlTextReader(StringReader reader, string defaultNamespace) : base(reader)
{
DefaultNamespace = defaultNamespace;
}
public override string NamespaceURI => String.IsNullOrEmpty(base.NamespaceURI) ? DefaultNamespace : base.NamespaceURI;
};
[XmlRoot(ElementName = "root", Namespace = "my_test_ns1")]
public class Test
{
[XmlElement("name", Namespace = "")] // Set namespace to empty string when no namespace prefix specified in xml
public string name { get; set; }
[XmlElement("age", Namespace = "")] // Set namespace to empty string when no namespace prefix specified in xml
public int age { get; set; }
[XmlElement(Namespace = "my_test_ns2")]
public int ageInMonths { get; set; }
public override bool Equals(object obj)
{
return obj is Test b && name == b.name && age == b.age && ageInMonths == b.ageInMonths;
}
public void Run(string str, string name)
{
using (StringReader rStream = new StringReader(str))
{
//using (XmlTextReader tr = new CustomXmlTextReader(rStream, "my_test_ns1"))
using (XmlTextReader tr = new XmlTextReader(rStream))
{
XmlSerializer serializer = new XmlSerializer(typeof(Test));
Test test = serializer.Deserialize(tr) as Test;
Console.Out.WriteLine(test.Equals(this) ? $"{name} equals expected" : $"{name} does not equal expected");
}
}
}
}
class Program
{
const string xml = #"<?xml version=""1.0"" encoding=""UTF-8"" ?>
<d:root xmlns:d=""my_test_ns1"" xmlns:v=""my_test_ns2"">
<name>Bill</name>
<age>32</age>
<v:ageInMonths>384</v:ageInMonths>
</d:root>
";
static void Main(string[] args)
{
Test expected = new Test()
{
name = "Bill",
age = 32,
ageInMonths = 384
};
expected.Run(xml, "xml1");
}
}
}

Related

C# XML Serialization How To Set Attribute xsi:type

This is how my Xml should look after XML Serialization:
<value xsi:type="CD" otherAttributes= "IDK">
.
.
.
</value>
Thats my C# code to it:
public class Valué
{
[XmlAttribute(AttributeName ="xsi:type")]
public string Type { get; set; } = "CD";
[XmlAttribute(attributeName: "otherAttributes")]
public string OtherAttributes { get; set; } = "IDK"
}
Apparently the XmlSerializer can't serialize colons (:) in attributenames.... how do i fix this problem?
If i remove the colon from the attributeName itm works out fine ..
Do it the correct way :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
Valué value = new CD() { OtherAttributes = "IDK" };
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(FILENAME, settings);
XmlSerializer serializer = new XmlSerializer(typeof(Valué));
serializer.Serialize(writer, value);
}
}
[XmlInclude(typeof(CD))]
public class Valué
{
}
public class CD : Valué
{
[XmlAttribute(attributeName: "otherAttributes")]
public string OtherAttributes { get; set; }
}
}

Read xml attribute as object

I have the following simple XML file and I am tying to read the attribute code, using a C# .NET Core Console. I really appetite for any help.
<course type="IT" date="19.09.2019">
<line code="IT001"/>
</course>
UPDATE
I need the result as an object.
Use xml serialization to get a class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Globalization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Course));
Course course = (Course)serializer.Deserialize(reader);
}
}
[XmlRoot("course")]
public class Course
{
[XmlAttribute("type")]
public string _type { get; set; }
public DateTime _date { get; set; }
[XmlAttribute("date")]
public string date {
get { return _date.ToString("dd.MM.yyyy"); }
set { _date = DateTime.ParseExact(value, "dd.MM.yyyy", System.Globalization.CultureInfo.InvariantCulture); }
}
private string _code { get; set; }
[XmlElement("line")]
public Line line
{
get { return new Line() { code = _code }; }
set { _code = value.code; }
}
}
[XmlRoot("line")]
public class Line
{
[XmlAttribute("code")]
public string code { get; set; }
}
}
Many ways to skin this cat. Here is a solution by making use of XPath
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml("<course type=\"IT\" date=\"19.09.2019\"> <line code=\"IT001\"/></course>");
var attrVal = xmlDoc.SelectSingleNode("/course/line/#code").Value;
Console.WriteLine("Attribute 'code' value is " + attrVal);

Consuming a SOAP service using HttpClient

I am having a problem with deserialization of an array which comes from the SOAP response. It returns null, but in response I can actually see a correct xml message. Maybe someone can see what's wrong in my code. Thanks in advance.
SOAP response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns-wp="imcwp" xmlns:ns-hostax="imchostax" xmlns:ns-ilms="imcilms" xmlns:ns-qtms="imcqtms" xmlns:ns-tptms="imctptms">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns-wp:strTrailerRequest-TrailerResponse>
<ns-wp:Trailer>
<ns-wp:TrailerId>T001</ns-wp:TrailerId>
<ns-wp:TrailerType>Flat Extender</ns-wp:TrailerType>
</ns-wp:Trailer>
<ns-wp:Trailer>
<ns-wp:TrailerId>T002</ns-wp:TrailerId>
<ns-wp:TrailerType>Flat Extender</ns-wp:TrailerType>
</ns-wp:Trailer>
<ns-wp:Trailer>
<ns-wp:TrailerId>T003</ns-wp:TrailerId>
<ns-wp:TrailerType>Flat Extender</ns-wp:TrailerType>
</ns-wp:Trailer>
</ns-wp:strTrailerRequest-TrailerResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Response Model:
[XmlRoot(ElementName = "strTrailerRequest-TrailerResponse", Namespace = "imcwp")]
public class strTrailerRequestTrailerResponse
{
[XmlArray("strTrailerRequest-TrailerResponse", Namespace = "imcwp")]
[XmlArrayItem("Trailer", Namespace = "imcwp")]
public List<Trailer> Trailers { get; set; }
}
Parse Method:
private strTrailerRequestTrailerResponse ParseTrailerResponse(string response)
{
var soap = XDocument.Parse(response);
XNamespace ns = "imcwp";
var trailerResponseNode = soap.Descendants(ns + "strTrailerRequest-TrailerResponse").FirstOrDefault().ToString();
var result = Deserialize<strTrailerRequestTrailerResponse>(trailerResponseNode);
return result;
}
Why not just deserilize the whole object, in that case u dont need xDocument and querying:
var envelop = Deserialize<Envelope>(response);
foreach (var strTrailerRequestTrailerResponseTrailer in envelop.Body.strTrailerRequestTrailerResponse)
{
}
and yr Deserialize method:
public static T Deserialize<T>(string response)
{
var serializer = new XmlSerializer(typeof(T));
using(TextReader reader = new StringReader(response))
{
return (T)serializer.Deserialize(reader);
}
}
If you still want to go with yr way using XDocument, it's ok yr Deserialize method should the same as I defined. if you wont try:
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "imcwp")]
public partial class strTrailerRequestTrailerResponseTrailer
{
public string TrailerId { get; set; }
public string TrailerType { get; set; }
}
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "imcwp")]
[System.Xml.Serialization.XmlRootAttribute("strTrailerRequest-TrailerResponse", Namespace = "imcwp", IsNullable = false)]
public partial class strTrailerRequestTrailerResponse
{
[System.Xml.Serialization.XmlElementAttribute("Trailer")]
public strTrailerRequestTrailerResponseTrailer[] Trailer { get; set; }
}
For simple xml you can use xml linq. See code below :
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace Certificate
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string xml = File.ReadAllText(FILENAME);
XDocument doc = XDocument.Parse(xml);
XElement soap = doc.Root;
XNamespace ns = soap.GetNamespaceOfPrefix("ns-wp");
List<Trailer> trailers = doc.Descendants(ns + "Trailer").Select(x => new Trailer()
{
trailerId = (string)x.Element(ns + "TrailerId"),
trailerType = (string)x.Element(ns + "TrailerType")
}).ToList();
}
}
public class Trailer
{
public string trailerId { get; set; }
public string trailerType { get;set;}
}
}

c# xml serialization add namespace to nodes

I have two class : Osoba, test
public class test
{
public string raz { get; set; }
public string dwa { get; set; }
}
public class Osoba
{
public test tehe { get; set; }
}
I also add namespaces to main root and seralize
Osoba ne = new Osoba();
test t1 = new praca2.test();
t1.dwa = "fgfg";
t1.raz = "dfdfdfdf";
ne.tehe = t1;
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("dfs", "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution");
ns.Add("d", "http://schemas.microsoft.com/office/infopath/2009/WSSList/dataFields");
ns.Add("pc", "http://schemas.microsoft.com/office/infopath/2007/PartnerControls");
XmlSerializer xsSubmit = new XmlSerializer(typeof(Osoba));
var xml = #"D:\dupa1.xml";
using (var stream = new FileStream(xml, FileMode.Create))
{
using (XmlWriter writer = XmlWriter.Create(stream))
{
xsSubmit.Serialize(writer, ne,ns);
xml = stream.ToString(); // Your XML
}
}
I get
<?xml version="1.0" encoding="utf-8"?>
<Osoba xmlns:dfs="http://schemas.microsoft.com/office/infopath/2003/dataFormSolution"xmlns:pc="http://schemas.microsoft.com/office/infopath/2007/PartnerControls"xmlns:d="http://schemas.microsoft.com/office/infopath/2009/WSSList/dataFields">
<tehe>
<raz>dfdfdfdf</raz>
<dwa>fgfg</dwa>
</tehe>
</Osoba>
I want add to node namespaces examle:
...
<pc:tehe>
<dfs:raz>dfdfdfdf</dfs:raz>
<dfs:dwa>fgfg</dfs:dwa>
</pc:tehe>
How I can do it?
I try add class atribute which set namespace
[XmlRoot("Node", Namespace="http://flibble")]
but it bad idea
You're almost there you just need to modify your classes slightly:
public class test
{
[XmlElement(Namespace = "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution")]
public string raz { get; set; }
[XmlElement(Namespace = "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution")]
public string dwa { get; set; }
}
public class Osoba
{
[XmlElement(Namespace = "http://schemas.microsoft.com/office/infopath/2007/PartnerControls")]
public test tehe { get; set; }
}
Sample implementation copied mostly from yours:
class Program
{
static void Main(string[] args)
{
Osoba ne = new Osoba();
test t1 = new test();
t1.dwa = "fgfg";
t1.raz = "dfdfdfdf";
ne.tehe = t1;
XmlSerializer xsSubmit = new XmlSerializer(typeof(Osoba));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("dfs", "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution");
ns.Add("pc", "http://schemas.microsoft.com/office/infopath/2007/PartnerControls");
var xml = #"D:\dupa1.xml";
using (var stream = new FileStream(xml, FileMode.Create))
{
using (XmlTextWriter writer = new XmlTextWriter(stream, Encoding.UTF8))
{
xsSubmit.Serialize(writer, ne, ns);
}
}
}
}
You will get this XML:
<?xml version="1.0" encoding="utf-8"?>
<Osoba xmlns:dfs="http://schemas.microsoft.com/office/infopath/2003/dataFormSolution" xmlns:pc="http://schemas.microsoft.com/office/infopath/2007/PartnerControls">
<pc:tehe>
<dfs:raz>dfdfdfdf</dfs:raz>
<dfs:dwa>fgfg</dfs:dwa>
</pc:tehe>
</Osoba>
Hth...
Uisng xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication42
{
class Program
{
static void Main(string[] args)
{
string xmlHeader =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<Osoba xmlns:dfs=\"http://schemas.microsoft.com/office/infopath/2003/dataFormSolution\"" +
" xmlns:pc=\"http://schemas.microsoft.com/office/infopath/2007/PartnerControls\"" +
" xmlns:d=\"http://schemas.microsoft.com/office/infopath/2009/WSSList/dataFields\">" +
"</Osoba>";
XDocument doc = XDocument.Parse(xmlHeader);
XElement osoba = doc.Root;
XNamespace dfsNs = osoba.GetNamespaceOfPrefix("dfs");
XNamespace pcNs = osoba.GetNamespaceOfPrefix("pc");
osoba.Add(new XElement(pcNs + "tehe", new object[] {
new XElement(dfsNs + "raz", "dfdfdfdf"),
new XElement(dfsNs + "dwa", "fgfg")
}));
}
}
}
I think you are new at C#.I suggest you to study C# basics, design patterns and repository pattern.For your requirement you can use below codes
public class Tehe
{
public string Raz { get; set; }
public string Dwa { get; set; }
}
public class TeheRepository
{
private System.Xml.Linq.XDocument xmlDatas;
public TeheRepository(string filePath)
{
xmlDatas = XDocument.Load(filePath);
}
public IEnumerable<Tehe> FindAll()
{
return xmlDatas.Elements().Elements().Select(tehe =>
{
Tehe t = new Tehe();
t.Dwa = tehe.Elements().ElementAt(1).Value;
t.Raz = tehe.Elements().ElementAt(0).Value;
return t;
});
}
}

XML deserialization failed to load values to element containing array of elements

I have the following XML file:
<?xml version="1.0" encoding="UTF-8"?>
<TestConfiguration xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Barcode>MB-B3-00</Barcode>
<TestSuites>
<Test>USB A Slave Port</Test>
<Test>USB B Host Port</Test>
</TestSuites>
</TestConfiguration>
I want to deserialize it into the following class:
public class TestConfiguration
{
private string _barcode;
private string[] _testSuites;
private string[] _testcase;
//Product barcode
public string Barcode
{
get{return this._barcode;}
set{this._barcode = value;}
}
//Test suites
[System.Xml.Serialization.XmlArrayItemAttribute("Test", IsNullable = false)]
public string[] Testsuites
{
get{return this._testSuites;}
set{this._testSuites = value;}
}
//individual test
[System.Xml.Serialization.XmlTextAttribute()]
public string[] Testcase
{
get{return this._testcase;}
set{this._testcase = value;}
}
}
My deserialization code is:
XmlSerializer serializer = new XmlSerializer(typeof(TestConfiguration));
StreamReader reader = new StreamReader(filename);
TestConfiguration _testConfig = (TestConfiguration)serializer.Deserialize(reader);
reader.Close();
However, _testConfig object only contains Barcode value and the properties Testcase and TestSuites are null. Any advice please?
You are very close. The name of your property, Testsuites, doesn't quite match the name of the element <TestSuites> - the capitalization of the letter S differs, and XML Tags are Case Sensitive.
To fix, rename the property or attach an XmlArrayAttribute with the correct element name:
//Test suites
[System.Xml.Serialization.XmlArray("TestSuites")]
[System.Xml.Serialization.XmlArrayItem("Test", IsNullable = false)]
public string[] Testsuites
{
get { return this._testSuites; }
set { this._testSuites = value; }
}
Try this. You can eliminate the tag and have an array of just element. I can show you how.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlSerializer serializer = new XmlSerializer(typeof(TestConfiguration));
StreamReader reader = new StreamReader(FILENAME);
TestConfiguration _testConfig = (TestConfiguration)serializer.Deserialize(reader);
reader.Close();
}
}
[XmlRoot("TestConfiguration")]
public class TestConfiguration
{
private string _barcode;
private string[] _testSuites;
private string[] _testcase;
//Product barcode
[XmlElement("Barcode")]
public string Barcode { get; set; }
//Test suites
[XmlElement("TestSuites")]
public TestSuites testSuites { get; set; }
}
//individual test
[XmlRoot("TestSuites")]
public class TestSuites
{
[XmlElement("Test")]
public List<string> test {get;set;}
}
}
​

Categories

Resources