Deserialization of XML - c#

Trying to deserialize a XML log file. And cannot seem to get anything but Error in XML Document(0,0). I am guessing it has something to do with my class but I cannot seem to find a solution. I cannot change the XML formatting as this is a log file coming from a server (just simplified)
XML
<?xml version="1.0" encoding="utf-8"?>
<POSLog>
<Transaction>
<RetailStoreID>1</RetailStoreID>
<SequenceNumber>2</SequenceNumber>
</Transaction>
<Transaction>
<RetailStoreID>1</RetailStoreID>
<SequenceNumber>3</SequenceNumber>
</Transaction>
</POSLog>
Class
[Serializable()]
public class Transaction
{
[XmlElement("RetailStoreID")]
public string RetailStoreID { get; set; }
[XmlElement("SequenceNumber")]
public string SequenceNumber { get; set; }
}
[Serializable()]
[XmlRoot("POSLog")]
public class POSLog
{
[XmlArray("POSLog")]
[XmlArrayItem("Transaction", typeof(Transaction))]
public Transaction[] Transaction { get; set; }
}
Deserialize code
POSLog poslog = new POSLog();
string path = "POSLog.xml";
XmlSerializer serializer = new XmlSerializer(typeof(POSLog));
StreamReader reader = new StreamReader(path);
poslog = (POSLog)serializer.Deserialize(reader);
Found fix by switching from StreamReader to FileStream along with changes to the class. Changing encoding didn't seem to help when using the StreamReader.

I cannot reproduce the problem you are seeing. However, there is an issue with the POSLog class -- it needs to be defined as follows:
[Serializable()]
[XmlRoot("POSLog")]
public class POSLog
{
[XmlElement("Transaction")]
public Transaction[] Transaction { get; set; }
}
Your XML has a root element <POSLog> containing a repeating sequence of <Transaction> elements. [XmlElement("Transaction")] maps the array to just such a one-level repeating sequence.
Example fiddle.

Changing the Class to the follow above answer
[Serializable()]
[XmlRoot("POSLog")]
public class POSLog
{
[XmlElement("Transaction")]
public Transaction[] Transaction { get; set; }
}
Along with Changing the following StreamReader lines
StreamReader reader = new StreamReader(path);
poslog = (POSLog)serializer.Deserialize(reader);
To this:
FileStream fs = new FileStream(path, FileMode.Open);
poslog = (POSLog)serializer.Deserialize(fs);
Fixed the issue I was having with the root element and I was able to deserialize the XML. Thanks to dbc for help in getting me started solving a solution for the first time in a forum!

Related

Deserialize part of SOAP response

I am a bit lost with the deserialization of a particular part of my soap response.
The response:
<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body>
<ns1:LoginResult xmlns:ns1="http://abc.def.schema">
<sessionId>123456789</sessionId>
<sessionTimeout>30</sessionTimeout>
<organizationName>WebService Test Account XYZ</organizationName>
<userInfoResult>
<accessibleOrgs>
<name>WebService Test Account XYZ</name>
<description/>
<prefix>10</prefix>
<countryCallingCode>+49</countryCallingCode>
<treeLevel>0</treeLevel>
<timeZone>
<timeZoneId>Europe/Berlin</timeZoneId>
<currentUtcOffset>3600000</currentUtcOffset>
</timeZone>
<billingCompany>COMPANY 123</billingCompany>
<language>DE</language>
</accessibleOrgs>
<isDemo>true</isDemo>
<prefixLength>0</prefixLength>
<alarmNumberLength>0</alarmNumberLength>
<groupNumberLength>0</groupNumberLength>
<personNumberLength>0</personNumberLength>
</userInfoResult>
</ns1:LoginResult>
</soapenv:Body>
I need to deserialize the "LoginResult" part. I am aware of deserialization methods but I am struggling with the fact that A) there are namespaces and B) I just need a subset of the XML.
Maybe somebody could point me into the right direction.
Thx in advcance
Start with definition of LoginResult class.
[XmlRootAttribute(Namespace = "http://abc.def.schema", IsNullable = false, ElementName = "LoginResult")]
public class LoginResult
{
[XmlElement(Namespace ="")]
public int sessionId { get; set; }
[XmlElement(Namespace = "")]
public string organizationName { get; set; }
..... some more properties
}
Use XDocument class from System.Xml.Linq to parse the xml.
Find the "LoginResult" element.
Deserialize as LoginResult type.
var xDoc = XDocument.Parse(str);
var xLoginResult = xDoc.Root.Descendants().FirstOrDefault(d => d.Name.LocalName.Equals("LoginResult"));
var serializer = new XmlSerializer(typeof(LoginResult));
using (var reader = xLoginResult.CreateReader())
{
var result = (LoginResult)serializer.Deserialize(reader);
}

Unit testing XML deserialisation

I am new to unit testing and I am wondering what would be the best practices for unit testing xml deserialisation.
Consider the following xml:
<people>
<person id="1">
<name>Joe</name>
<age>28</age>
</person>
<person id="2">
<name>Jack</name>
<age>38</age>
</person>
</people>
And the following model class for the people:
[XmlRoot(ElementName ="people")]
public class People
{
public People() { PeopleList = new List<Person>(); }
[XmlElement("person")]
public List<Person> PeopleList { get; set; }
}
public class Person
{
[XmlAttribute("id")]
public int id { get; set; }
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("age")]
public int Age { get; set; }
}
I deserialize the xml using:
public List<Person> GetListOfPeople()
{
People plist = new People();
string content;
using (StreamReader sr = new StreamReader(manager.Open("People.xml")))
{
var serializer = new XmlSerializer(typeof(People));
plist = (People)serializer.Deserialize(sr);
}
return plist.PeopleList;
}
What would be the best methods to unit test the GetListOfPeople method above?
If you can change your method to take an xml file as an input parameter, you can have a sample xml file created and added to your test project. Since you know the values of your xml file, you can start comparing the values directly.
Considering you'll use the sample file you provided in your test, you can verify like this:
var persons = x.GetListOfPeople("sample.xml");
Assert.AreEqual("Joe", persons[0].Name);
Assert.AreEqual(38, persons[1].Age);
If the xml file is coming to your code from some source and you think it couldn't be following your xml schema all the time, then probably you can create some sample xml files again which violate your schema and prepare tests to call your method which should throw some exception if schema is not correct.
Hope this helps.

XML Deseriazlier exception C#

I'm using C# (WPF).
I have the following XML file:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationToRun IsFromXML="true">
<Apps>
<FulllPath>c:\file1.txt</FullPath>
<FulllPath>c:\file2.txt</FullPath>
<FulllPath>c:\file3.txt</FullPath>
</Apps>
</ApplicationToRun>
And i try to deseriazlier the xml file.My Code:
mlSerializer xs = new XmlSerializer(typfof(MyXMLClass));
StringReader st = new StringReader(xmlPath);
MyXMLClass apps = (MyXMLClass)xs.Deserialize(st); //Exception - System.Windows.Markup.XamlParseException
Inside the inner exception: System.InvalidOperationException :There is an error in XML document (1.1) ...
My Classes:
[XmlRootAttriute("ApplicationToRun:)
public class MyXMLClass
{
[XmlAttribute]
public bool IsFromXML {get;set;}
[XmlElement("Apps")]
public FullPath [] fullPath {get;set;}
}
public class FullPath
{
public stirng fullPathApp {set;get;}
}
Where is my mistake?
Thanks!
The XML is badly formed, but all it takes is a bit of cleaning up:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationToRun IsFromXML="true">
<Apps>
<FullPath>foo</FullPath>
<FullPath>bar</FullPath>
</Apps>
</ApplicationToRun>
Then assign the correct XML attributes to your class:
[XmlRootAttribute("ApplicationToRun")]
public class MyXMLClass
{
[XmlAttribute]
public bool IsFromXML { get; set; }
[XmlArray("Apps")]
[XmlArrayItem("FullPath")]
public string[] fullPath { get; set; }
}
There's a typo in the elements. The opening elements have 3 Ls (FulllPath) but the closing ones have 2 Ls (as they should) that's causing the XML error

Reading large XML files with C#

I would like to know how can I read a XML file from my desktop and put it into a string?
Here is my XML:
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
I want to store each user, but still detect if their small or big, is there a way to do this?
Before you downrate this, you might want to check google because I did research, but found nothing.
"Before you downrate this, you might want to check google because I
did research, but found nothing"
You found nothing because you don't know what you are searching for, also your XML is invalid, you need to enclose it in a rootElement. Then the first thing you need to do is read this file from the desktop if it exists.
You can check the size if you wish at that time and determine if this is "too large" even though it doesn't really matter. I highly doubt your XML file will be 5+ GB in size. If it is then you need an alternative, no single object in a .Net program may be over 2GB, the best you could do is 1,073,741,823 on a 64bit machine.
For very large XML files, anything above 1.0 GB, combine XmlReader and LINQ as stated by Jon Skeet here:
If your document is particularly huge, you can combine XmlReader and
LINQ to XML by creating an XElement from an XmlReader for each of your
"outer" elements in a streaming manner: this lets you do most of the
conversion work in LINQ to XML, but still only need a small portion of
the document in memory at any one time.
For small XML files, anything 1.0 GB or lower stick to the DOM as shown below.
With that said, what you need is to learn what Serialization and Deserialization mean.
Serialize convert an object instance to an XML document.
Deserialize convert an XML document into an object instance.
Instead of XML you can also use JSON, binary, etc.
In your case this is what can be done to Deserialize this XML document back into an Object in order for you to use in your code.
First fix up the XML and give it a Root.
<?xml version="1.0" encoding="UTF-8"?>
<DataRoot>
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
</DataRoot>
Then create the root class in C#, you may generate this directly in Visual Studio 2012+ by copying your XML and going to Edit - Paste Special, but I like to use: XML to C# Class Generator
Here is what your code would look like after you generate the C# Root Class for your XML, hope it helps you understand it better.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public class Program
{
[XmlRoot(ElementName = "user")]
public class User
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "motto")]
public string Motto { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
}
[XmlRoot(ElementName = "smallusers")]
public class Smallusers
{
[XmlElement(ElementName = "user")]
public List<User> User { get; set; }
}
[XmlRoot(ElementName = "bigusers")]
public class Bigusers
{
[XmlElement(ElementName = "user")]
public User User { get; set; }
}
[XmlRoot(ElementName = "DataRoot")]
public class DataRoot
{
[XmlElement(ElementName = "smallusers")]
public Smallusers Smallusers { get; set; }
[XmlElement(ElementName = "bigusers")]
public Bigusers Bigusers { get; set; }
}
static void Main(string[] args)
{
string testXMLData = #"<DataRoot><smallusers><user id=""1""><name>John</name><motto>I am john, who are you?</motto></user><user id=""2""><name>Peter</name><motto>Hello everyone!</motto></user></smallusers><bigusers><user id=""3""><name>Barry</name><motto>Earth is awesome</motto></user></bigusers></DataRoot>";
var fileXmlData = File.ReadAllText(#"C:\XMLFile.xml");
var deserializedObject = DeserializeFromXML(fileXmlData);
var serializedToXML = SerializeToXml(deserializedObject);
//I want to store each user, but still detect if their small or big, is there a way to do this?
foreach (var smallUser in deserializedObject.Smallusers.User)
{
//Iterating your collection of Small users?
//Do what you need here with `smalluser`.
var name = smallUser.Name; //Example...
}
Console.WriteLine(serializedToXML);
Console.ReadKey();
}
public static string SerializeToXml(DataRoot DataObject)
{
var xsSubmit = new XmlSerializer(typeof(DataRoot));
using (var sw = new StringWriter())
{
using (var writer = XmlWriter.Create(sw))
{
xsSubmit.Serialize(writer, DataObject);
var data = sw.ToString();
writer.Flush();
writer.Close();
sw.Flush();
sw.Close();
return data;
}
}
}
public static DataRoot DeserializeFromXML(string xml)
{
var xsExpirations = new XmlSerializer(typeof(DataRoot));
DataRoot rootDataObj = null;
using (TextReader reader = new StringReader(xml))
{
rootDataObj = (DataRoot)xsExpirations.Deserialize(reader);
reader.Close();
}
return rootDataObj;
}
}
}

Not being able to deserealize this XML

Just for some fun I was playing with the API of Last.fm. The XML file they return for top artists is structured like this:
<lfm status="ok">
<topartists user="xbonez" type="overall">
<artist rank="1">
<name>Evanescence</name>
<playcount>4618</playcount>
<mbid>f4a31f0a-51dd-4fa7-986d-3095c40c5ed9</mbid>
<url>http://www.last.fm/music/Evanescence</url>
<streamable>1</streamable>
<image size="small">http://userserve-ak.last.fm/serve/34/48488613.png</image>
<image size="medium">http://userserve-ak.last.fm/serve/64/48488613.png</image>
<image size="large">http://userserve-ak.last.fm/serve/126/48488613.png</image>
<image size="extralarge">http://userserve-ak.last.fm/serve/252/48488613.png</image>
<image size="mega">http://userserve-ak.last.fm/serve/500/48488613/Evanescence++PNG.png</image>
</artist>
</topartists>
</lfm>
This is how I'm deserealizing it.
I have a class called lfmStatus:
[Serializable()]
[XmlRootAttribute("lfm")]
public class lfmStatus
{
[XmlElement("artist")]
public List<Artists> TopArtists { get; set; }
}
And a Class Artists:
[Serializable()]
public class Artists
{
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("playcount")]
public int playcount { get; set; }
[XmlElement("url")]
public string url { get; set; }
[XmlElement("streamable")]
public int streamable { get; set; }
[XmlElement("image")]
public string image { get; set; }
}
And then I deserealize using this code:
string XmlFile = "artists.xml";
XmlSerializer serializer = new XmlSerializer(typeof(lfmStatus));
lfmStatus LoadFile;
using (Stream reader = new FileStream(XmlFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
try
{
Console.WriteLine("Beginning deserialization.");
// Call the Deserialize method to restore the object's state.
LoadFile = (lfmStatus)serializer.Deserialize(reader);
return LoadFile.TopArtists;
}
Now, this code works great for the XML if it did not have the topartists tag enveloping all the artists. But since it does, how do I change my code to handle that? I'm assuming I need to add another class.
You are missing the attribute(s) on a few types.
See XmlAttributeAttribute for more detail.
You are also missing the topartists element's type.
If I was you, I would get the XML schema and just use xsd.exe to generate the C# classes, and modify from there. It can also infer the schema based on XML if you really can't find it, this will give you a parsable result based on the input XML.
To see that you wrote correct code for deserializing the response XML you can use XSD. Open VS command prompt and give XSD LastFM.xml which generates and XSD file. Now give XSD LastFM.XSD which will generate a CS file. compare that one with the one you have written and check if you made any mistakes.

Categories