Can't Deserialize xml using DataContractSerializer - c#

I can't deserialize this XML to an object, I don't know what is wrong with this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<ProcessOneWayEvent xmlns="http://schemas.microsoft.com/sharepoint/remoteapp/">
<properties xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<CultureLCID>1033</CultureLCID>
</properties>
</ProcessOneWayEvent>
</s:Body>
</s:Envelope>
Here is a ready sample for this, is there any workaround, because I can't modify in the request, so is there any thing wrong with my models? And is there any solution to deserialize the XML without using XmlSerializer
https://dotnetfiddle.net/RfQMSD

Body and SPRemoteEventProperties need to be in the "http://schemas.microsoft.com/sharepoint/remoteapp/" namespace:
[DataContract(Name = "Body", Namespace = "http://schemas.microsoft.com/sharepoint/remoteapp/")]
public class Body
{
[DataMember(Name = "ProcessOneWayEvent")]
public ProcessOneWayEvent ProcessOneWayEvent;
}
[DataContract(Name = "properties", Namespace = "http://schemas.microsoft.com/sharepoint/remoteapp/")]
public class SPRemoteEventProperties
{
[DataMember(Name = "CultureLCID") ]
public int CultureLCID { get; set; }
}
The DataContractAttribute.Namespace controls the namespace that the data contract object's data member elements are serialized to, as well as the namespace of the root element when the data contract object is the root element. Since the element <ProcessOneWayEvent xmlns="http://schemas.microsoft.com/sharepoint/remoteapp/"> declares a new default XML namespace, the element itself, as well as its children, are in this namespace. Thus, the containing data contract object Body must set its data member namespace accordingly. As for <properties xmlns:i="http://www.w3.org/2001/XMLSchema-instance">, the i: namespace is not a default namespace, so no elements actually get assigned to this namespace and the SPRemoteEventProperties type should not not assign itself to it.
Fixed fiddle here.

I don't immediately see why it doesn't work, but one alternative is to use System.Xml.Serialization.XmlSerializer.
Start by creating the appropriate Envelope classes by copying the XML into a new class (Edit → Paste Special → Paste XML as Classes)
Then deserialize using this as an example:
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(request);
using (var stream = new MemoryStream(byteArray))
{
var serializer = new XmlSerializer(typeof(Envelope));
Envelope response = (Envelope)serializer.Deserialize(stream);
Console.WriteLine(JsonConvert.SerializeObject(response));
}

Try following :
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)
{
string xml = File.ReadAllText(FILENAME);
StringReader sReader = new StringReader(xml);
XmlReader reader = XmlReader.Create(sReader);
XmlSerializer serializer = new XmlSerializer(typeof(Envelope));
Envelope envelope = (Envelope)serializer.Deserialize(reader);
}
}
[XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope
{
[XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public Body Body { get; set; }
}
public class Body
{
[XmlElement(ElementName = "ProcessOneWayEvent", Namespace = "http://schemas.microsoft.com/sharepoint/remoteapp/")]
public ProcessOneWayEvent ProcessOneWayEvent { get; set; }
}
public class ProcessOneWayEvent
{
public Properties properties { get; set; }
}
public class Properties
{
public string CultureLCID { get; set; }
}
}
Using Xml Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string xml = File.ReadAllText(FILENAME);
XDocument doc = XDocument.Parse(xml);
string CultureLCID = (string)doc.Descendants().Where(x => x.Name.LocalName == "CultureLCID").FirstOrDefault();
}
}
}

Related

How to add namespaces to XML before serialization

I do have a problem with serialization of a namespaces. As my code show below my logic fills the structure of my dictionary, serializes it and puts into a string variable. I use this variable to load into XMLDocument and after that I do add the namespaces. But since they are added after the serialization process the namespaces are set in UTF8? Should I add namespaces before serialization? If yes, how can I do it properly?
///Dictionary
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace TestXML
{
[Serializable]
[XmlRoot(ElementName = "Root")]
public class XMLSchema: Serialization
{
[XmlElement(ElementName = "Element1")]
public XMLElement1 Element1 { get; set; }
[XmlElement(ElementName = "Element2")]
public XMLElement2 Element2 { get; set; }
}
}
///Serialization class
public class Utf8StringWriter : StringWriter
{
// Use UTF8 encoding
public override Encoding Encoding
{
get { return new UTF8Encoding(false); }
}
}
public string Serialize()
{
var xmlserializer = new XmlSerializer(this.GetType());
var Utf8StringWriter = new Utf8StringWriter();
var xns = new XmlSerializerNamespaces();
xns.Add(string.Empty, string.Empty);
using (var writer = XmlWriter.Create(Utf8StringWriter))
{
xmlserializer.Serialize(writer, this, xns);
return Utf8StringWriter.ToString();
}
}
///create xml
str xml;
[...] my logic to add data into elements
XmlDocument doc = new XmlDocument();
xml = XMLSchema.Serialize();
doc.LoadXml(xml);
XmlElement root = doc.getNamedElement("Root");
root.SetAttribute("xmlns:etd", "http://google.com");
root.SetAttribute("xmlns:xsi", "http://google.com");
root.SetAttribute("xmlns", "http://google.com");
doc.AppendChild(root);
doc.Save(path);
Edit. Adding provided sample.
<?xml version="1.0" encoding="UTF-8"?> <XMLSample xmlns="http://crd.gov.pl/wzor/" xmlns:xsi="http://www.w3.org/2001/" xmlns:etd="http://crd.gov.pl/xml/schematy/">
So to give you an example (based on your sample):
[XmlRoot("XMLSample", Namespace = "http://crd.gov.pl/wzor/")]
public class XmlSample
{
[XmlElement]
public string Element1 { get; set; }
[XmlElement(Namespace = "http://crd.gov.pl/xml/schematy/")]
public string Element2 { get; set; }
}
Here, the root has the namespace http://crd.gov.pl/wzor/. Element1 inherits that namespace (as none is specified). Element2 has the namespace http://crd.gov.pl/xml/schematy/.
When this is serialised, the serialiser will use the root namespace as the default, so there's no need to explicitly set this. You can set the others to use the prefixes as defined in your sample:
var xsn = new XmlSerializerNamespaces();
xsn.Add("xsi", "http://www.w3.org/2001/");
xsn.Add("etd", "http://crd.gov.pl/xml/schematy/");
You can see this fiddle for a demo, the output is:
<?xml version="1.0" encoding="utf-8"?>
<XMLSample xmlns:xsi="http://www.w3.org/2001/" xmlns:etd="http://crd.gov.pl/xml/schematy/" xmlns="http://crd.gov.pl/wzor/">
<Element1>foo</Element1>
<etd:Element2>bar</etd:Element2>
</XMLSample>
Note that Element2 uses the prefix configured for its namespace.

How to create a class which is to be de-serialized from XML

How to create a C# class which must be used to deserialize the XML as given below
<?xml version="1.0" encoding="utf-8"?>
<XML>
<StatusCode>-2</StatusCode>
<Warnings />
<Errors>
<Error> Debtor #2 Invalid Postal Code</Error>
<Error>Invalid lien term</Error>
</Errors>
</XML>
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication110
{
class Program
{
const string INPUT_FILENAME = #"c:\temp\test.xml";
const string OUTPUT_FILENAME = #"c:\temp\test1.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(INPUT_FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(XML));
XML xml = (XML)serializer.Deserialize(reader);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(OUTPUT_FILENAME, settings);
serializer.Serialize(writer, xml);
}
}
public class XML
{
public int StatusCode { get; set; }
public string Warnings { get; set; }
[XmlArray("Errors")]
[XmlArrayItem("Error")]
public List<string> errors { get; set; }
}
}
To create classes based on XML, copy the xml to the clipboard, then in Visual Studio 2017, choose the menu option: Edit/Paste Special/Paste XML as classes.
Your Class should look like:
public class ErrorClass
{
struct Error
{
public String message;
}
struct Warning
{
public String message;
}
int StatusCode;
List<Error> Errors;
List<Warning> Warnings;
}
Error and Warning struct could contain more items that are not used in the example you posted.

Identifier on XmlAttribute

I have an XML document I'm trying to deseralize that has a attribute that is ref which in C# can not be used to declare a variable hence the below doesn't work
[XmlAttribute()]
public string ref;
Anyway to get this to deseralize properly? I know it is case sensitive so Ref wouldn't work.
You can provide a name in the attribute:
[XmlAttribute("ref")]
public string anynameyouwant;
You can change the attribute name in the xml file, by using the AttributeName, such as in the following example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace soans
{
public class Test
{
//problematic attribute (ref is reserved)
[XmlAttribute(AttributeName="ref")]
public string RefAttr {get;set;}
//other attributes as well
[XmlAttribute()]
public string Field { get; set; }
}
class Program
{
static void Main(string[] args)
{
string filename = ""; //use your path here
Test original = new Test()
{
RefAttr = "ref",
Field = "test"
};
//serialiser
XmlSerializer ser = new XmlSerializer(typeof(Test));
//save to file
TextWriter writer = new StreamWriter(filename);
ser.Serialize(writer, original);
writer.Close();
//read from file
TextReader reader = new StreamReader(filename);
var fromfile = ser.Deserialize(reader) as Test;
if(fromfile!=null)
{
Console.WriteLine(fromfile.RefAttr);
}
reader.Close();
Console.ReadKey();
}
}
}

Float XML Namespaces to Root?

I have XML within a C# program that looks something like this:
<el1 xmlns="http://URI1">
<el2 xmlns:namespace2="http://URI2"></el2>
<el3>
<el4 xmlns:namespace3="http://URI3"></el4>
</el3>
</el1>
For cleanliness, I would like to move all the namespace declarations to the root element. I cannot change the export that produces this XML, so a solution needs to work on the sample as shown above. What is a good way to accomplish this?
This example is stripped down for brevity, but assume that there are further child elements that actually use these prefixes. These are irrelevant for this question as all the namespace prefix declarations are unique and my goal is only to move them higher in the tree.
I've reviewed the MSDN documentation for XML but there doesn't seem to be a simple way to manipulate namespaces like this. One of the solutions I've tried is interacting with the XML as an XElement and collecting the namespaces based on XAttribute.IsNamespaceDeclaration, replacing each element with its local name and finally creating a new root element with the collected list of namespace XAttributes. That line of experimentation caused a bunch of errors about redefining prefixes, though, and I'm not sure if I'm moving in the right direction or not.
You need to add a prefix
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication2
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
El1 el1 = new El1()
{
el2 = new El2()
{
el3 = new El3() {
el4 = new El4() {
}
}
}
};
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("u4", "http://URI4");
ns.Add("u3", "http://URI3");
ns.Add("u2", "http://URI2");
ns.Add("", "http://URI1");
XmlSerializer serializer = new XmlSerializer(typeof(El1));
StreamWriter writer = new StreamWriter(FILENAME);
serializer.Serialize(writer, el1, ns);
writer.Flush();
writer.Close();
writer.Dispose();
}
}
[XmlRoot("el1", Namespace = "http://URI1")]
public class El1
{
[XmlElement("el2", Namespace = "http://URI2")]
public El2 el2 { get; set; }
}
[XmlRoot("el2")]
public class El2
{
[XmlElement("el3", Namespace = "http://URI3")]
public El3 el3 { get; set; }
}
[XmlRoot("el3", Namespace = "http://URI3")]
public class El3
{
[XmlElement("el4", Namespace = "http://URI4")]
public El4 el4 { get; set; }
}
[XmlRoot("el4", Namespace = "http://URI1")]
public class El4
{
}
}
​
You can locate all the xmls attributes using the namespace xpath axis. Add those attributes to the root element. Finally write out the xml using NamespaceHandling.OmitDuplicates, which will leave the namespace declarations on the root and remove them from all the other elements.
var xml = new XmlDocument();
xml.Load("XMLFile1.xml");
// Find all xmlns: attributes
var attributes = xml.DocumentElement.SelectNodes("//namespace::*");
// Add xmlns: attributes to the root
foreach (XmlAttribute attribute in attributes)
xml.DocumentElement.SetAttribute(attribute.Name, attribute.Value);
// Write out results, ignoring duplicate xmlns: attributes
var settings = new XmlWriterSettings();
settings.NamespaceHandling = NamespaceHandling.OmitDuplicates;
settings.Indent = true;
using (var writer = XmlWriter.Create("XMLFile2.xml", settings))
{
xml.Save(writer);
}

How to change XML root name with XML Serialization?

I am trying to change the root name when doing XML serialization with C#.
It always takes the class name and not the name I am trying to set it with.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyTest test = new MyTest();
test.Test = "gog";
List<MyTest> testList = new List<MyTest>()
{
test
};
SerializeToXML(testList);
}
static public void SerializeToXML(List<MyTest> list)
{
XmlSerializer serializer = new XmlSerializer(typeof(List<MyTest>));
TextWriter textWriter = new StreamWriter(#"C:\New folder\test.xml");
serializer.Serialize(textWriter, list);
textWriter.Close();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
[XmlRootAttribute(ElementName = "WildAnimal", IsNullable = false)]
public class MyTest
{
[XmlElement("Test")]
public string Test { get; set; }
}
}
Result
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMyTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyTest>
<Test>gog</Test>
</MyTest>
</ArrayOfMyTest>
It does not change it to WildAnimal. I am not sure why. I got this from a tutorial.
Edit # Marc
Thanks. I now see what your doing just seems so odd that you have to make a wrapper around it. I have one more question what happens if I wanted to make this format
<root>
<element>
<name></name>
</element>
<anotherElement>
<product></product>
</anotherElement>
</root>
so like a nested elements. Would I have to make a new class for the second part and stick that in the wrapper class too?
In your example, MyTest is not the root; are you trying to rename the array? I would write a wrapper:
[XmlRoot("NameOfRootElement")]
public class MyWrapper {
private List<MyTest> items = new List<MyTest>();
[XmlElement("NameOfChildElement")]
public List<MyTest> Items { get { return items; } }
}
static void Main() {
MyTest test = new MyTest();
test.Test = "gog";
MyWrapper wrapper = new MyWrapper {
Items = { test }
};
SerializeToXML(wrapper);
}
static public void SerializeToXML(MyWrapper list) {
XmlSerializer serializer = new XmlSerializer(typeof(MyWrapper));
using (TextWriter textWriter = new StreamWriter(#"test.xml")) {
serializer.Serialize(textWriter, list);
textWriter.Close();
}
}

Categories