Serialize Class to flat XML - c#

Essentially, I need to serialize a C# object to an xml document with an entirely different xml structure and different class/node names. The structure of the C# class is:
public class Root
{
public item item {get; set}
}
public class item
{
public string name {get; set}
public color[] color
}
public class color
{
public string itemColor {get; set}
}
Lets say our item is a car. This serializes to
<Root>
<item>
<name>car</name>
<color>
<itemColor>red</itemColor>
<itemColor>blue</itemColor>
<itemColor>gree</itemColor>
</Color>
</item>
</Root>
But I need this to serialize to:
<Root>
<item>
<name>car</name>
<itemColor>red</itemColor>
</item>
<name>car</name>
<itemColor>blue</itemColor>
</item>
<item>
<name>car</name>
<itemColor>green</itemColor>
</item>
</Root>
I'm currently using IXmlSerializable to try and specify a schema. What is the optimal way of doing this? Should I convert to a second custom object?

Try this:
public class Root
{
[XmlIgnore]
public item item { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
[XmlAnyElement("item")]
public List<XElement> _item
{
get
{
return item.color.Select(i =>
new XElement("item",
new XElement("name", item.name),
new XElement("itemColor", i.itemColor)
)).ToList();
}
}
}
public class item
{
public string name { get; set; }
public color[] color;
}
public class color
{
public string itemColor { get; set; }
}
Also, it would be better to use nameof instead of hardcoded literals.
[XmlAnyElement(nameof(item))]
public List<XElement> _item
{
get
{
return item.color.Select(i =>
new XElement(nameof(item),
new XElement(nameof(item.name), item.name),
new XElement(nameof(i.itemColor), i.itemColor)
)).ToList();
}
}

Related

Cannot deserialize sub-elements to list of objects

I am having problems serializing elements of an XML to a list of objects.
This is the XML:
<result>
<stats>
<numitemsfound>1451</numitemsfound>
<startfrom>0</startfrom>
</stats>
<items>
<item>
<id>1</id>
<markedfordeletion>0</markedfordeletion>
<thumbsrc>
</thumbsrc>
<thumbsrclarge>
</thumbsrclarge>
...
<datasource>65</datasource>
<data>
<amount>100</amount>
<kj>389</kj>
<kcal>92.91</kcal>
<fat_gram>0.2</fat_gram>
<fat_sat_gram>-1</fat_sat_gram>
<kh_gram>20.03</kh_gram>
</data>
<servings>
<serving>
<serving_id>386</serving_id>
<weight_gram>150</weight_gram>
</serving>
</servings>
</item>
</result>
The classes I prepared for serialization are
[XmlType("item")]
public class Item
{
[XmlAttribute("id")]
public string id { get; set; }
[XmlAttribute("markedfordeletion")]
public string markedfordeletion { get; set; }
...
[XmlAttribute("datasource")]
public string datasource { get; set; }
[XmlElement("data")]
public Data data { get; set; }
[XmlElement("servings")]
public List<Serving> servings { get; set; }
}
}
This is how I try to serialize the xml
public void ParseSearch(string xml)
{
XmlSerializer serializer = new XmlSerializer(typeof(List<Item>), new XmlRootAttribute("item"));
StringReader stringReader = new StringReader(xml);
var productList = (List<Item>)serializer.Deserialize(stringReader);
}
But I get the error ----> System.InvalidOperationException : <result xmlns=''> was not expected. Can you please help me solve this problem?
You have to use a serializer which serializes an instance of result, not of type List:
XmlSerializer serializer = new XmlSerializer(typeof(Result), new XmlRootAttribute("result")); //whatever `Result` actually is as type).
You canĀ“t serialize and de-serialize just parts of a document, either the whole one or nothing at all.
So you need a root-type:
[XmlRoot("result")]
public class Result
{
public Stats Stats {get; set;}
[XmlArray("items")]
[XmlArrayItem("item")]
public List<Item> Items { get; set; }
}

How to serialize a XML node of repeated elements

I am trying to deserialize some XML into an array of items.
Here's the XML:
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<items>
<item>
<name>John</name>
</item>
<item>
<name>Jane</name>
</item>
</items>
And my class:
[XmlRoot("item")]
public class Item
{
[XmlElement("name")]
public string Name { get; set; }
}
I then deserialize:
var xmlSerializer = new XmlSerializer(typeof(Item[]), new XmlRootAttribute("items"));
using (TextReader textReader = new StringReader(xmlString))
{
var items = (Item[])xmlSerializer.Deserialize(textReader);
var itemCount = items.Length;
}
itemCount is 0 (it should be 2).
There is a similar solution here: https://stackoverflow.com/questions/15544517 but it seem to work only when the XML node names are identical to the class names (mine differ in capitalisation).
What do I need to modify to ensure all the items deserialize?
The Xml Root "items" is missing
Your class should be:
[XmlRoot("items")]
public class Items
{
[XmlElement("item")]
public Item[] Item { get; set; }
}
[XmlRoot("item")]
public class Item
{
[XmlElement("name")]
public string Name { get; set; }
}
And the code to deserialize:
var xmlSerializer = new XmlSerializer(typeof(Items), new XmlRootAttribute("items"));
using (TextReader textReader = new StringReader(xmlString))
{
var items = (Items)xmlSerializer.Deserialize(textReader);
var itemCount = items.Item.Length;
}
The root node is <items>, so the Item class should not have the XmlRoot attribute, instead it should use the XmlType attribute:
[XmlType("item")]
public class Item
{
[XmlElement("name")]
public string Name { get; set; }
}

How to deserialize an XML with a nested array elements?

Deserialization of XML file with nested array of elements
I've searched but can't see any helpful examples.
this is my XML sample:
<trans>
<orderNo>0001</orderNo>
<orderDate>08/07/2014</orderDate>
<orders>
<item>
<itemName>item1</itemName>
<itemAmount>200</itemAmount>
<itemMeasures>
<measure>each</measure>
<measure>case</measure>
</itemMeasures>
</item>
<item>
<itemName>item2</itemName>
<itemAmount>100</itemAmount>
<itemMeasures>
<measure>each</measure>
<measure>case</measure>
</itemMeasures>
</item>
</orders>
</trans>
You must create classes for each of the structures you have in the XML and then using XmlSerializer and the method Deserialize of the object from that class you can create nested arrays with the values. If you want code and example, please edit your post with the full xml structure.
See the example below for part of your xml:
[XmlType("trans")]
public class Trans
{
[XmlElement("orderNo")]
public string OrderNo { get; set; }
[XmlElement("orderDate")]
public string OrderDate { get; set; }
[XmlArray("orders")]
public HashSet<Item> Orders { get; set; }
}
[XmlType("item")]
public class Item
{
[XmlElement("itemName")]
public string ItemName { get; set; }
[XmlElement("itemAmount")]
public string ItemAmount { get; set; }
}
The the Deserialization code is:
XmlSerializer mySerializer = new XmlSerializer(typeof(Trans[]), new XmlRootAttribute("trans"));
using (FileStream myFileStream = new FileStream("XmlFile.xml", FileMode.Open))
{
Trans[] r;
r = (Trans[])mySerializer.Deserialize(myFileStream);
}

deserializing a nested list in xml

I am c# silverlight beginner i am under a situation that i have thios xml code:
string xmlstring = #"<?xml version='1.0' encoding='utf-8' ?>
<par>
<name>amount</name>
<label>Amount</label>
<unit>Hundred</unit >
<comp>
<type>Combo</type>
<attributes>
<type>Integer</type>
<displayed>4</displayed>
<selected>0</selected>
<item>5</item>
</attributes>
</comp>
</par>";
** Now what is the problem ?**
In the last line when i try to debug "item" which is asssigned several value like 5 on line Debug.WriteLine(attrib.item);i just see only "5" on debugging it dont show other values. I guess i need to implement a list for it. But how to do it here that i don't know. Could some one please help me so that i will be able to have all the assigned values to item in this c# code because after this step i havce to create a GUI of it. Woudl be a big help.
Note: I cannot use ArrayList because silverligth dont support it any other lastertnative please ?
This is what you need:
[XmlRoot(ElementName = "parameter")]
public class Parameter
{
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("label")]
public string Label { get; set; }
[XmlElement("unit")]
public string Unit { get; set; }
[XmlElement("component")]
public Component Component { get; set; }
}
[XmlRoot(ElementName = "component")]
public class Component {
[XmlElement("type")]
public string Type { get; set; }
[XmlElement("attributes")]
public Attributes Attributes { get; set; }
}
[XmlRoot(ElementName = "attributes")]
public class Attributes
{
[XmlElement("type")]
public string Type { get; set; }
[XmlElement("displayed")]
public string Displayed { get; set; }
[XmlElement("selected")]
public string Selected { get; set; }
[XmlArray("items")]
[XmlArrayItem("item", typeof(string))]
public List<string> Items { get; set; }
}
class Program
{
static void Main(string[] args)
{
string xmlstring = #"<?xml version='1.0' encoding='utf-8' ?>
<parameter>
<name>max_amount</name>
<label>Max Amount</label>
<unit>Millions</unit>
<component>
<type>Combo</type>
<attributes>
<type>Integer</type>
<displayed>4</displayed>
<selected>0</selected>
<items>
<item>5</item>
<item>10</item>
<item>20</item>
<item>50</item>
</items>
</attributes>
</component >
</parameter>";
XmlSerializer deserializer = new XmlSerializer(typeof(Parameter));
XmlReader reader = XmlReader.Create(new StringReader(xmlstring));
Parameter parameter = (Parameter)deserializer.Deserialize(reader);
Console.WriteLine("Type: {0}", parameter.Component.Attributes.Type);
Console.WriteLine("Displayed: {0}", parameter.Component.Attributes.Displayed);
Console.WriteLine("Selected: {0}", parameter.Component.Attributes.Selected);
Console.WriteLine("Items: ");
foreach (var item in parameter.Component.Attributes.Items)
{
Console.WriteLine("\t{0}", item);
}
Console.ReadLine();
}
}
Note small change in xmlstring, now every <item></item> is inside container:
<items>
<item>5</item>
<item>10</item>
<item>20</item>
<item>50</item>
</items>

Deserialize XML to object (need to return a list of objects)

Started practicing with XML and C# and I have an error message of "There is an error in XML document (3,2)". After looking at the file, I can't see anything wrong with it (Mind you, I probably missed something since I'm a noob). I'm using a Console Application for C# right now. I'm trying to return a list of Adventurers and just a side note, the GEAR element is optional. Here is what I have so far:
XML File - Test1
<?xml version="1.0" encoding="utf-8"?>
<Catalog>
<Adventurer>
<ID>001</ID>
<Name>John Smith</Name>
<Address>123 Fake Street</Address>
<Phone>123-456-7890</Phone>
<Gear>
<Attack>
<Item>
<IName>Sword</IName>
<IPrice>15.00</IPrice>
</Item>
<Item>
<IName>Wand</IName>
<IPrice>20.00</IPrice>
</Item>
</Attack>
<Defense>
<Item>
<IName>Shield</IName>
<IPrice>5.00</IPrice>
</Item>
</Defense>
</Gear>
</Adventurer>
<Adventurer>
<ID>002</ID>
<Name>Guy noone likes</Name>
<Address>Some Big House</Address>
<Phone>666-666-6666</Phone>
<Gear></Gear>
</Adventurer>
</Catalog>
C# Classes
public class Catalog
{
List<Adventurer> Adventurers { get; set; }
}
public class Adventurer
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public Gear Gear { get; set; }
}
public class Gear
{
public List<Item> Attack { get; set; }
public List<Item> Defense { get; set; }
}
public class Item
{
public string IName { get; set; }
public decimal IPrice { get; set; }
}
Serialize Function - Where the Problem Occurs at Line 5
Catalog obj = null;
string path = #"C:\Users\Blah\Desktop\test1.xml";
XmlSerializer serializer = new XmlSerializer(typeof(Catalog));
StreamReader reader = new StreamReader(path);
obj = (Catalog)serializer.Deserialize(reader);
reader.Close();
Console.ReadLine();
The issue is the list of Adventurers in Catalog:
<?xml version="1.0" encoding="utf-8"?>
<Catalog>
<Adventurers> <!-- you're missing this -->
<Adventurer>
</Adventurer>
...
<Adventurer>
</Adventurer>
</Adventurers> <!-- and missing this -->
</Catalog>
You don't have the wrapping element for the Adventurers collection.
EDIT: By the way, I find the easiest way to build the XML structure and make sure it's compatible is to create the object(s) in C#, then run through the built-in XmlSerializer and use its XML output as a basis for any XML I create rather than forming it by hand.
First, "Adventurers" property is not public, it's inaccessible, I think that the best way to find the error is to serialize your object and then compare the result with your xml file.
Your XML doesn't quite line up with your objects... namely these two...
public string City { get; set; }
and
<Address>123 Fake Street</Address>
Change City to Address or vice versa and it should fix the problem.
Edit: Got this to work in a test project, combination of all our answers...
Add <Adventurers> tag after <Catalog> (and </Adventurers> before </Catalog>) and change
List<Adventurer> Adventurers { get; set; }
to
public List<Adventurer> Adventurers { get; set; }
and it works properly for me.
I was able to get your xml to deserialize with a couple minor changes (namely the public qualifier on Adventurer).
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml.Serialization;
namespace TempSandbox
{
[XmlRoot]
public class Catalog
{
[XmlElement("Adventurer")]
public List<Adventurer> Adventurers;
private readonly static Type[] myTypes = new Type[] { typeof(Adventurer), typeof(Gear), typeof(Item) };
private readonly static XmlSerializer mySerializer = new XmlSerializer(typeof(Catalog), myTypes);
public static Catalog Deserialize(string xml)
{
return (Catalog)Utils.Deserialize(mySerializer, xml, Encoding.UTF8);
}
}
[XmlRoot]
public class Adventurer
{
public int ID;
public string Name;
public string Address;
public string Phone;
[XmlElement(IsNullable = true)]
public Gear Gear;
}
[XmlRoot]
public class Gear
{
public List<Item> Attack;
public List<Item> Defense;
}
[XmlRoot]
public class Item
{
public string IName;
public decimal IPrice;
}
}
I'm using [XmlElement("Adventurer")] because the xml element names don't exactly match the class property names.
NOTE: I'm using a generic deserialization utility i already had on hand .

Categories