how do i deserialize this xml? - c#

I need to deserialize the xml below. I want to use xmlserializer because I am (more) familiar with it.
I believe this xml is not constructed correctly however I cannot change it.
The below represents a list of category objects. When I try to deserialize using
xmlserializer(typeof(List<Category>))
I get this error: "categories xmlns='' is not expected"
<?xml version="1.0" encoding="utf-8" ?>
<categories>
<category id="16" name="Exports" parent_id="13"/>
<category id="17" name="Imports" parent_id="13"/>
<category id="3000" name="Income Payments & Receipts" parent_id="13"/>
<category id="125" name="Trade Balance" parent_id="13"/>
<category id="127" name="U.S. International Finance" parent_id="13"/>
</categories>
I don't mind making some kind of dummy class to deserilize these if that is what I have to do.
Here is my Category Class
[XmlType("category")]
public class Category
{
[XmlAttribute("id")]
public int ID { get; set; }
[XmlAttribute("parent_id")]
public int ParentID { get; set; }
[XmlAttribute("name")]
public string Name { get; set; }
}
My code:
XmlSerializer serializer = new XmlSerializer(typeof(List<Category>));
StringReader reader = new StringReader(xml);
List<Category> obj = null;
using (System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(reader))
{
obj = (List<Category>)serializer.Deserialize(xmlReader);
}
return obj;

You can just pass in the XmlRootAttribute into the serializer for the "categories" part.
BUT... you must remove the "&" from your xml because its not valid
XmlSerializer serializer = new XmlSerializer(typeof(List<Category>), new XmlRootAttribute("categories"));
using (FileStream fileStream = new FileStream(#"C:\Test.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
{
var test = serializer.Deserialize(fileStream);
}
Here is your method working with a String.Replace to sort out the "&"
private List<Category> GetCategories(string xmlData)
{
List<Category> obj = null;
XmlSerializer serializer = new XmlSerializer(typeof(List<Category>), new XmlRootAttribute("categories"));
StringReader reader = new StringReader(xmlData.Replace("&","&"));
using (System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(reader))
{
obj = (List<Category>)serializer.Deserialize(xmlReader);
}
return obj;
}

Try to make a categories class that will contain your List<Category> like this:
[XmlRoot("categories")]
public class Categories
{
public Categories()
{
Items = new List<User>();
}
[XmlElement("category")]
public List<Category> Items {get;set;}
}
You can than create a serializer like this:
XmlSerializer serializer = new XmlSerializer(typeof(Categories));

Do you have an XSD that this XML should conform to? If so, you can generate the required code using:
"xsd your.xsd /classes"

Related

I am getting InvalidOperation error on deserializing a xml string to class object

I am continuously getting invalidOperation error while converting XML to a class object.
I have tried : Deserialize XML String into Class but it didn't work
Below is my code for deserializing
XElement categoryList = XElement.Load(stream);
XNamespace ebayns = "urn:ebay:apis:eBLBaseComponents";
XElement list = categoryList.Descendants(ebayns + "CategoryArray").First();
XmlSerializer xmlSerializer = new XmlSerializer(typeof(CategoryList));
using (StringReader stringReader = new StringReader(list.ToString()))
{
CategoryList _categoryNode = (CategoryList)xmlSerializer.Deserialize(stringReader);
}
below is my CategoryList Model
[XmlRoot("CategoryArray")]
public class CategoryList
{
[XmlElement("Category")]
public List<Category> categoryList = new List<Category>();
}
Sample Xml request
<CategoryArray xmlns="urn:ebay:apis:eBLBaseComponents">
<Category xmlns="urn:ebay:apis:eBLBaseComponents">
<CategoryID>-1</CategoryID>
<CategoryLevel>0</CategoryLevel>
<CategoryName>Root</CategoryName>
<CategoryParentID>0</CategoryParentID>
<LeafCategory>false</LeafCategory>
</Category>
<Category xmlns="urn:ebay:apis:eBLBaseComponents">
<CategoryID>20081</CategoryID>
<CategoryLevel>1</CategoryLevel>
<CategoryName>Antiques</CategoryName>
<CategoryParentID>-1</CategoryParentID>
<CategoryNamePath>Antiques</CategoryNamePath>
<CategoryIDPath>20081</CategoryIDPath>
<LeafCategory>false</LeafCategory>
</Category>
</CategoryArray>
I need to get a list of category class object

C# serialize object to XML with element containing XML text without escaping

I searched and tried some attributes but none of them worked for my below scenario:
public class ObjSer
{
[XmlElement("Name")]
public string Name
{
get; set;
}
}
//Code to serialize
var obj = new ObjSer();
obj.Name = "<tag1>Value</tag1>";
var stringwriter = new System.IO.StringWriter();
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(stringwriter, obj);
Output would be as follows:
<ObjSer><Name><tag1>Value</tag1></Name></ObjSer>
But I need output as:
<ObjSer><Name><tag1>Value</tag1></Name></ObjSer>
Scenario 2: In some cases I need to set:
obj.Name = "Value";
Is there any attribute or method I can override to make it possible?
You can't with default serializer. XmlSerializer does encoding of all values during serialization.
If you want your member to hold xml value, it must be XmlElement. Here is how you can accomplish it
public class ObjSer
{
[XmlElement("Name")]
public XmlElement Name
{
get; set;
}
}
var obj = new ObjSer();
// <-- load xml
var doc = new XmlDocument();
doc.LoadXml("<tag1>Value</tag1>");
obj.Name = doc.DocumentElement;
// --> assign the element
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(Console.Out, obj);
Output:
<?xml version="1.0" encoding="IBM437"?>
<ObjSer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>
<tag1>Value</tag1>
</Name>
</ObjSer>
UPDATE:
In case if you want to use XElement instead of XmlElement, here is sample on how to do it
public class ObjSer
{
[XmlElement("Name")]
public XElement Name
{
get; set;
}
}
static void Main(string[] args)
{
//Code to serialize
var obj = new ObjSer();
obj.Name = XDocument.Parse("<tag1>Value</tag1>").Document.FirstNode as XElement;
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(Console.Out, obj);
}
No, you can't. It is the natural and usual behaviour of the xml serializer. The serializer doesn't have to handle within the XML strings. So, it escapes the xml as expected.
You should decode the escaped string again while deserializing it.
If you want to add dynamically elements in the XML I suggest you to use Linq to XML and you can create tag1 or another kind of elements easily.
I would suggest serializing to an XDocument then converting that to a string and manually unescaping the desired part and writing it to a file. I would say this would give you the least headache it shouldn't be more than a couple lines of code. If you need it I can provide some code example if needed.
I found one more way of changing the type
public class NameNode
{
public string tag1
{
get; set;
}
[XmlText]
public string Value
{
get; set;
}
}
public class ObjSer
{
[XmlElement("Name")]
public NameNode Name
{
get; set;
}
}
To set value of Name:
var obj = new ObjSer();
var valueToSet = "<tag1>Value</tag1>";
//or var valueToSet = "Value";
//With XML tag:
if (valueToSet.Contains("</"))
{
var doc = new XmlDocument();
doc.LoadXml(valueToSet);
obj.Name.tag1 = doc.InnerText;
}
else //Without XML Tags
{
obj.Name.Value = senderRecipient.Name;
}
This solution will work in both cases, but has limitation. It will work only for predefined tags (ex. tag1)

Writing/ Reading Multiple Objects into Xml C#

I am new to Xml and have written the code whcih creates an Xml and Reads it back also.
But i want to have some modifications in the Xml structure.
What i don't want is ArrayOfMovie tag, which is coming as the root tab.
But when i am writing multiple objects into the Xml it shows an ArrayOfMovie tag. As i have to maintain the structure of the class, the upper tag as Movie, then its details and then other movie. Also please if you tell the code to modify the xml, tell the procedure to read the newly structured xml too.
Here is the code for the scenario:
// Movies class which contains the list of Movie objects
public class Movies
{
public List<Movie> movieList = new List<Movie>();
}
//Movie class
public class Movie
{
public string Title
{ get; set; }
public int Rating
{ get; set; }
public DateTime ReleaseDate
{ get; set; }
}
private void CreateXml_Click(object sender, EventArgs e)
{
string filePath = path + textBox_XmlFileName.Text+".xml";
Movie firstMov = new Movie();
firstMov.Title = "Shrek";
firstMov.Rating = 2;
firstMov.ReleaseDate = DateTime.Now;
Movie secondMov = new Movie();
secondMov.Title = "Spider Man";
secondMov.Rating = 4;
secondMov.ReleaseDate = DateTime.Now;
Movies moviesObj = new Movies();
moviesObj.movieList.Add(firstMov);
moviesObj.movieList.Add(secondMov);
List<Movie> movList = new List<Movie>() { firstMov,secondMov};
XmlHandler.SerializeToXml(moviesObj.movieList, filePath);
}
// The static class and funcion that creates the xml file
public static void SerializeToXml(List<Movie> movies ,string filePath)
{
XmlSerializer xls= new XmlSerializer(typeof(List<Movie>));
TextWriter tw = new StreamWriter(filePath);
xls.Serialize(tw, movies);
tw.Close();
}
// It Creates the following output
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMovie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Movie>
<Title>Shrek</Title>
<Rating>2</Rating>
<ReleaseDate>2014-05-25T22:55:17.2811063+05:00</ReleaseDate>
</Movie>
<Movie>
<Title>Spider Man</Title>
<Rating>4</Rating>
<ReleaseDate>2014-05-25T22:55:17.2811063+05:00</ReleaseDate>
</Movie>
</ArrayOfMovie>
// The code for reading the file into objects
public static List<Movie> DeserializeFromXml(string filePath)
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<Movie>));
TextReader tr = new StreamReader(#filePath);
List<Movie> movie;
movie = (List<Movie>)deserializer.Deserialize(tr);
tr.Close();
return movie;
}
you may use the XmlRootAttribute if you want to name your root
XmlSerializer deserializer = new XmlSerializer(typeof(List<Movie>),
new XmlRootAttribute("YourRoot"));

How to include attributes for an element (not the root) with XML Serialization in C#

I need to generate an XML that looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<inboundMessage xmlns="http://www.myurl.net">
<header>
<password>mypwd</password>
<subscriberId>myuser</subscriberId>
</header>
<message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="myType">
<eventDate>2012-09-05T12:13:45.561-05:00</eventDate>
<externalEventId />
<externalId>SomeIdC</externalId>
</message>
</inboundMessage>
The problem is that I don't know how to include the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="myType" in my tag. My class that I need to serialize is this:
[XmlType("inboundMessage")]
[XmlRoot(Namespace = "http://www.myurl.net")]
public class InboundMessage
{
[XmlElement(ElementName = "header")]
public Header _header;
[XmlElement(ElementName = "message")]
public List<MyType> _messages;
}
What XmlAttributes do I need to add to my "_messages" member to make it serialize the way I want?
TIA,
Ed
Use XmlAttribute like this:
public class MyType
{
[XmlAttribute("type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Type { get; set; }
}
A coleague of mine came up with a somewhat similar solution, yet more complete. MyType had two properties added:
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces { get; set; }
[XmlAttribute("type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Type
{
get { return _type; }
set { _type = value; }
}
with _type being defined like this:
private string _type = "myType";
Then the serialization was done in a different way:
// declare an XmlAttributes object, indicating that the namespaces declaration should be kept
var atts = new XmlAttributes { Xmlns = true };
// declare an XmlAttributesOverrides object and ...
var xover = new XmlAttributeOverrides();
// ... add the XmlAttributes object with regard to the "Namespaces" property member of the "Message" type
xover.Add(typeof(MyType), "Namespaces", atts);
// set the Namespaces property for each message in the payload body
var messageNamespaces = new XmlSerializerNamespaces();
messageNamespaces.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
foreach (var message in myInboundMessage._messages)
{
message.Namespaces = messageNamespaces;
}
// create a serializer
var serializer = new XmlSerializer(object2Serialize.GetType(), xover);
// add the namespaces for the root element
var rootNamespaces = new XmlSerializerNamespaces();
rootNamespaces.Add("", "http://www.myurl.net");
// serialize and extract the XML as text
string objectAsXmlText;
using (var stream = new MemoryStream())
using (var xmlTextWriter = new XmlTextWriter(stream, null))
{
serializer.Serialize(xmlTextWriter, object2Serialize, rootNamespaces);
stream.Seek(0, SeekOrigin.Begin);
var buffer = new byte[stream.Length];
stream.Read(buffer, 0, (int)stream.Length);
objectAsXmlText = Encoding.UTF8.GetString(buffer);
}
Finally, the InboundMessage was decorated like this:
[XmlRoot("inboundMessage", Namespace = "http://www.myurl.net")]
With this approach I get exactely what I need.

Serialize List<T> to XML, and reverse the XML to List<T>

Does anyone know how I (or if it's possible to) reverse the XML I'm creating below
[Serializable()]
public class CustomDictionary
{
public string Key { get; set; }
public string Value { get; set; }
}
public class OtherClass
{
protected void BtnSaveClick(object sender, EventArgs e)
{
var analysisList = new List<CustomDictionary>();
// Here i fill the analysisList with some data
// ...
// This renders the xml posted below
string myXML = Serialize(analysisList).ToString();
xmlLiteral.Text = myXML;
}
public static StringWriter Serialize(object o)
{
var xs = new XmlSerializer(o.GetType());
var xml = new StringWriter();
xs.Serialize(xml, o);
return xml;
}
}
The xml rendered
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfCustomDictionary xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CustomDictionary>
<Key>Gender</Key>
<Value>0</Value>
</CustomDictionary>
<CustomDictionary>
<Key>Height</Key>
<Value>4</Value>
</CustomDictionary>
<CustomDictionary>
<Key>Age</Key>
<Value>2</Value>
</CustomDictionary>
</ArrayOfCustomDictionary>
Now, after a few hours of Googling and trying I'm stuck (most likely my brain have some vacation already). Can anyone help me how to reverse this xml back to a List?
Thanks
Just deserialize it:
public static T Deserialize<T>(string xml) {
var xs = new XmlSerializer(typeof(T));
return (T)xs.Deserialize(new StringReader(xml));
}
Use it like this:
var deserializedDictionaries = Deserialize<List<CustomDictionary>>(myXML);
XmlSerializer.Deserialize

Categories