c# XML serialization - c#

I'd like to serialize something like this, where there is a header and a body.
The first part "galleryData" is the header
The 2nd part is "imageData" - repeated for each image in gallery
<galleryData>
<title>some title</title>
<uuid>32432322</uuid>
<imagepath>some path</imagepath>
</galleryData>
<imageData>
<title>title one</title>
<category>nature</category>
<description>blah blah</description>
</imageData>
<imageData>
<title>title two</title>
<category>nature</category>
<description>blah blah</description>
</imageData>
<imageData>
<title>title three</title>
<category>nature</category>
<description>blah blah</description>
</imageData>
I see how to do it if I didn't need a header area. I'm currently just using xmlwriter to create it, but I'd like to serialize the object out to xml instead.

You need a root in order to have valid XML. Here's an example of how your model might look like:
public class ImageData
{
[XmlElement("title")]
public string Title { get; set; }
[XmlElement("category")]
public string Category { get; set; }
[XmlElement("description")]
public string Description { get; set; }
}
public class GalleryData
{
[XmlElement("title")]
public string Title { get; set; }
[XmlElement("uuid")]
public string UUID { get; set; }
[XmlElement("imagepath")]
public string ImagePath { get; set; }
}
public class MyData
{
[XmlElement("galleryData")]
public GalleryData GalleryData { get; set; }
[XmlElement("imageData")]
public ImageData[] ImageDatas { get; set; }
}
and then simply create an instance of this model and serialize it to a stream:
class Program
{
static void Main()
{
var myData = new MyData
{
GalleryData = new GalleryData
{
Title = "some title",
UUID = "32432322",
ImagePath = "some path"
},
ImageDatas = new[]
{
new ImageData
{
Title = "title one",
Category = "nature",
Description = "blah blah"
},
new ImageData
{
Title = "title two",
Category = "nature",
Description = "blah blah"
},
}
};
var serializer = new XmlSerializer(myData.GetType());
serializer.Serialize(Console.Out, myData);
}
}

Given the way that XML serialization works, I do not believe the structure you are looking for will be possible from a straight Object -> XML structure as in your example you have more than one root node.
If you had something where there was a container node, then individual ImageData elements within them, or a single over arching element to bundle them together you might be able to get by with it.

If you are using XmlSerialization, you will need a root element - which will be the class that you serialize.
Your galleryData and imageData will be object instance variables within the class used for your root element.

You can create an object for header with properties an mark the with XmlSerializabe and the add a field of type List so that standart xml serialized will serialize it as child elements of header

You can use something like the following to serialize a .NET object to an XML string:
protected string ObjectToXml<T>(T obj)
{
var sw = new StringWriter();
try
{
var mySerializer = new XmlSerializer(typeof(T));
mySerializer.Serialize(sw, obj);
}
catch (Exception ex)
{
// Error logging here
}
return sw.ToString();
}
You will need a root element for your XML document, and if your current model doesn't match the desired serialized form, then create and populate an appropriate data transfer object to do the serialisation job.

The structure you're showing is not valid XML because is containing more then one root node, so you can forget about XmlSerializer. If you want to handle easily with such a xml similar structures I suggest Html Agility Pack

Related

C# Deserialze XML to an Object

I am trying to deserialize xml to an object using C#.
This is my XML.
<Products>
<Product>
<Name>Test</Name>
<Price Amount="12.95">£ 12.95</Price>
</Product>
</Products>
And this is my code.
class Program
{
static void Main(string[] args)
{
var filePath = #"C:\Eastpoint\TestApps\TestHunterSuppliers\bin\Debug\Sample.xml";
var reader = new XmlTextReader(filePath);
var serializer = new XmlSerializer(typeof(Products));
var products = (Products)serializer.Deserialize(reader);
Console.WriteLine(products.Product.Name);
Console.WriteLine(products.Product.Price.Amount);
}
}
public class Products
{
public Product Product { get; set; }
}
public class Product
{
public string Name { get; set; }
public Price Price { get; set; }
}
public class Price
{
public string Amount { get; set; }
public string Value { get; set; }
}
By using the above code I am getting the product object but properties of the price object are always deserializing to null values.
Could somebody advice me what I am missing.
Thanks,
Naresh
The default behavior of .NET's XML serializer is to serialize properties to XML elements. The value of the property becomes the inner text of the XML element corresponding to the property. That is, if you serialized your objects it would look like this:
<Products>
<Product>
<Name>Test</Name>
<Price>
<Amount>12.95</Amount>
<Value>£ 12.95</Value>
</Price>
</Product>
</Products>
In your case, you need to instruct the serializer to put Price.Amount into an attribute and to write Price.Value as Price's inner text. The easiest way to do this is to decorate the properties requiring non-default serialization with appropriate [XmlXxx] attributes:
...
[XmlAttribute]
public string Amount { get ; set ; }
[XmlText]
public string Value { get ; set ; }
...
Incidentally, if your Products is supposed to contain more than one product, you will need to modify your code like this:
...
[XmlElement ("Product")]
public Product[] All { get ; set ; }
...
The attribute instructs the serializer not to create an <All> element to hold the individual products' elements. You can also use other collections like List<Product>, but you should create them beforehand like this:
...
[XmlElement ("Product")]
public readonly List<Product> All = new List<Product> () ;
...
You can make your original test code working using FileStream,
Tested code sample:
var filePath = #"C:\Eastpoint\TestApps\TestHunterSuppliers\bin\Debug\Sample.xml";
var serializer = new XmlSerializer(typeof(Products));
//Setting dummy object to create the xml
Products myProducts = new Products { Product = new Product { Name = "Papers", Price = new Price { Amount = "10", Value = "20" } } };
var strWrite = new FileStream(filePath, FileMode.Create);
serializer.Serialize(strWrite, myProducts);
strWrite.Close();
//////////////////////////////
var strRead = new FileStream(filePath, FileMode.Open);
var products = (Products)serializer.Deserialize(strRead);
strRead.Close();
Console.WriteLine(products.Product.Name);
Console.WriteLine(products.Product.Price.Amount);
And to get the price as an attribute in the XML document:
public class Price
{
[XmlAttribute]
public string Amount { get; set; }
public string Value { get; set; }
}

Adding to XML file

I am making a WPF that searches through an XML file pulling out restaurant information. The XML is in this format:
<FoodPhoneNumbers>
<Restaurant Name="Pizza Place">
<Type>Pizza</Type>
<PhoneNumber>(123)-456-7890</PhoneNumber>
<Hours>
<Open>11:00am</Open>
<Close>11:00pm</Close>
</Hours>
</Restaurant>
</FoodPhoneNumbers>
I want to be able to add a new restaurant to the XML file. I have a textbox for the restaurant name, and type. Then three textboxes for the phone number. 4 comboboxes for the open hour, open minute, close hour, and close minute. I also have 2 listboxes for selecting AM or PM for the open and close times.
I assume I use XmlTextWriter, but I could not figure out how to add the text to a pre-existing XML file.
The simplest way isn't to use XmlTextWriter - it's just to load the whole into an in-memory representation, add the new element, then save. Obviously that's not terribly efficient for large files, but it's really simple if you can get away with it. For example, using XDocument:
XDocument doc = XDocument.Load("test.xml");
XElement restaurant = new XElement("Restaurant",
new XAttribute("Name", "Frenchies"),
new XElement("Type", "French"),
new XElement("PhoneNumber", "555-12345678"),
new XElement("Hours",
new XElement("Open", "1:00pm"),
new XElement("Close", "2:00pm")));
doc.Root.Add(restaurant);
doc.Save("test.xml");
Or, better:
XDocument doc = XDocument.Load("test.xml");
Restaurant restaurant = ...; // Populate a Restaurant object
// The Restaurant class could know how to serialize itself to an XElement
XElement element = restaurant.ToXElement();
doc.Root.Add(element);
The instance of XmlSerializer class can be used to achieve this.
[XmlRoot("FoodPhoneNumbers")]
public class FoodPhoneNumbers
{
[XmlElement(ElementName = "Restaurant")]
public List<Restaurant> Restaurants { get; set; }
}
public class Restaurant
{
[XmlAttribute]
public string Name { get; set; }
[XmlElement]
public string Type { get; set; }
[XmlElement]
public string PhoneNumber { get; set; }
[XmlElement(ElementName = "Hours")]
public List<Hours> Hours { get; set; }
}
public class Hours
{
[XmlElement]
public string Open { get; set; }
[XmlElement]
public string Close { get; set; }
}
Serialization/deserialization code:
// Deserialize.
FoodPhoneNumbers food;
using (Stream inputStream = File.OpenRead(inputFilePath))
food = (FoodPhoneNumbers) xmlSerializer.Deserialize(inputStream);
// Add a new one.
Restaurant restaurant = new Restaurant
{
Name = "Coffee restraurant",
PhoneNumber = "0xFF",
Type = "Coffee shop"
};
food.Restaurants.Add(restaurant);
// Serialize.
using (Stream outputStream = File.OpenWrite(outputFilePath))
xmlSerializer.Serialize(outputStream, food);

Element was not expected While Deserializing an Array with XML Serialization

OK. I'm trying to work on communicating with the Pivotal Tracker API, which only returns data in an XML format. I have the following XML that I'm trying to deserialize into my domain model.
<?xml version="1.0" encoding="UTF-8"?>
<stories type="array" count="2" total="2">
<story>
<id type="integer">2909137</id>
<project_id type="integer">68153</project_id>
<story_type>bug</story_type>
<url>http://www.pivotaltracker.com/story/show/2909137</url>
<current_state>unscheduled</current_state>
<description></description>
<name>Test #2</name>
<requested_by>Anthony Shaw</requested_by>
<created_at type="datetime">2010/03/23 20:05:58 EDT</created_at>
<updated_at type="datetime">2010/03/23 20:05:58 EDT</updated_at>
</story>
<story>
<id type="integer">2909135</id>
<project_id type="integer">68153</project_id>
<story_type>feature</story_type>
<url>http://www.pivotaltracker.com/story/show/2909135</url>
<estimate type="integer">-1</estimate>
<current_state>unscheduled</current_state>
<description></description>
<name>Test #1</name>
<requested_by>Anthony Shaw</requested_by>
<created_at type="datetime">2010/03/23 20:05:53 EDT</created_at>
<updated_at type="datetime">2010/03/23 20:05:53 EDT</updated_at>
</story>
</stories>
My 'story' object is created as follows:
public class story
{
public int id { get; set; }
public int estimate { get; set; }
public int project_id { get; set; }
public string story_type { get; set; }
public string url { get; set; }
public string current_state { get; set; }
public string description { get; set; }
public string name { get; set; }
public string requested_by { get; set; }
public string labels { get; set; }
public string lighthouse_id { get; set; }
public string lighthouse_url { get; set; }
public string owned_by { get; set; }
public string accepted_at { get; set; }
public string created_at { get; set; }
public attachment[] attachments { get; set; }
public note[] notes { get; set; }
}
When I execute my deserialization code, I receive the following exception:
Exception:
There is an error in XML document (2, 2).
Inner Exception:
<stories xmlns=''> was not expected.
I can deserialize the individual stories just fine, I just cannot deserialize this xml into an array of 'story' objects
And my deserialization code (value is a string of the xml)
var byteArray = Encoding.ASCII.GetBytes(value);
var stream = new MemoryStream(byteArray);
var deserializedObject = new XmlSerializer(typeof (story[])).Deserialize(stream)
Does anybody have any ideas?
Let me offer a more concise solution. Set your deserialization up to look like this:
var deserializedObject = new XmlSerializer(typeof(story[]), new XmlRootAttribute("stories")).Deserialize(stream);
By specifying that second parameter in the XmlSerializer, you can avoid having to stub out that class. It lets the serializer know what the root element's name is.
For this to work, the name of the class that represents the array-element type must exactly match the XML name, e.g. class story {}, <story>. You can get around this (and I'd recommend it as a best practice anyway) by specifying the XmlType:
[XmlType("story")]
public class Story
{
...
}
I prefer to do this as it frees me from being stuck with the XML type name.
The problem is that you have no property named "stories". The XML Serializer has no idea what to do with the stories element when it sees it.
One thing you could try is to create a "stories" class:
public class stories : List<story> {}
and use
var byteArray = Encoding.ASCII.GetBytes(value);
stories deserializedObject = null;
using (var stream = new MemoryStream(byteArray))
{
var storiesSerializer = new XmlSerializer(typeof (stories));
deserializedObject = (stories)storiesSerializer .Deserialize(stream);
}
Try something like
public class stories
{
[XmlElement("story")]
public story[] storyarray { get; set; }
}
...
var byteArray = Encoding.ASCII.GetBytes(value);
XmlSerializer serializer = new XmlSerializer(typeof(stories));
stories myStories = null;
using (var stream = new MemoryStream(byteArray))
{
myStories = (stories)serializer.Deserialize(stream);
}
foreach (story stor in myStories.storyarray)
Console.WriteLine(stor.story_type);
Edit: Updated code sample to use using statement based on feedback.
XMSerializer expects an XML Namespace with which to understand your XML from.
xmlns="http://schemas.microsoft.com"
... ought to do. See the XML sample at the bottom of this page.
I would recommend that you generate an XSD from some sample XML you get from the web service. Then, with that XSD, you can generate the classes that have all the proper serialization attributes affixed to them.
To generate a schema (unless you prefer to write your own), open the sample XML file in Visual Studio, and select the XML -> Create Schema menu option. Save that XSD.
To generate the classes, run the XSD command from the VS command prompt. If you run it without parameters, it'll show you the command-line parameters you can use.
Now you can create a type-safe XML serializer.

Inject XML attribute into serialization

I am trying to serialize an array and want to attach an attribute to the array. For example, the output I want is:
<ArrayOfThingie version="1.0">
<Thingie>
<name>one</name>
</Thingie>
<Thingie>
<name>two</name>
</Thingie>
</ArrayOfThingie>
This is just a primitive array, so I don't want to define the attribute for the array itself, just in its serialization. Is there a way to inject an attribute into the serialization?
You could create a wrapper for ArrayOfThingie just for serialization:
public class Thingie
{
[XmlElement("name")]
public string Name { get; set; }
}
[XmlRoot]
public class ArrayOfThingie
{
[XmlAttribute("version")]
public string Version { get; set; }
[XmlElement("Thingie")]
public Thingie[] Thingies { get; set; }
}
static void Main(string[] args)
{
Thingie[] thingies = new[] { new Thingie { Name = "one" }, new Thingie { Name = "two" } };
ArrayOfThingie at = new ArrayOfThingie { Thingies = thingies, Version = "1.0" };
XmlSerializer serializer = new XmlSerializer(typeof(ArrayOfThingie));
StringWriter writer = new StringWriter();
serializer.Serialize(writer, at);
Console.WriteLine(writer.ToString());
}
A bit of a hack would be to serialize the array to XML and then modify the serialized XML before saving. A cleaner way assuming the Array is a property of a class would be to Add an attribute to a serialized XML node.

What is the best way to manually parse an XElement into custom objects?

I have an XElement variable named content which consists of the following XML:
<content>
<title>Contact Data</title>
<p>This is a paragraph this will be displayed in front of the first form.</p>
<form idCode="contactData" query="limit 10; category=internal"/>
<form idCode="contactDataDetail" query="limit 10; category=internal">
<title>Contact Data Detail</title>
<description>This is the detail information</description>
</form>
</content>
I now want to simply run through each of the level-1 nodes and parse them into objects. Back in C# 2.0 I use to do this with XmlReader, check the type of node, and parse it accordingly.
But what is the best way to parse the XML nodes with LINQ, I would expect something like this:
var contentItems = from contentItem in pageItem.content.DescendantNodes()
select new ContentItem
{
Type = contentItem.Element()
};
foreach (var contentItem in contentItems)
{
switch (contentItem.Type)
{
case "title":
...(parse title)...
case "p":
...(parse p)...
case "form":
...(parse form)...
}
}
where:
public class ContentItem
{
public string Type { get; set; }
public string IdCode { get; set; }
public XElement Content { get; set; }
}
Does it have to be XElement? I would (either manually, or via xsd.exe) just create classes that map to the element/attribute names - and use XmlSerializer - in particular via StringReader:
Content content;
using(StringReader sr = new StringReader(xml))
using(XmlReader xr = XmlReader.Create(sr)) {
XmlSerializer ser = new XmlSerializer(typeof(Content));
content = (Content)ser.Deserialize(xr);
}
(edit)
With entity classes:
[XmlRoot("content")]
public class Content {
[XmlElement("title")]
public string Title { get; set; }
[XmlElement("p")]
public string Description { get; set; }
[XmlElement("form")]
public List<ContentForm> Forms { get; set; }
}
public class ContentForm {
[XmlAttribute("idCode")]
public string Id { get; set; }
[XmlAttribute("query")]
public string Query { get; set; }
[XmlElement("title")]
public string Title { get; set; }
[XmlElement("description")]
public string Description { get; set; }
}
I'd suggest inheriting XElement, and implement properties for the stuff you want in it.
These properties shouldn't use backing fields, but rather work directly with the underlying XML element. That way, you'll keep object in sync with XML.
With XML to LINQ one pulls specific data items out of the XML, rather than iterating through the XML looking for what is found.
var results = from node in XmlDocument.SomeContext(...)
select new MyType {
Prop1 = node.Element("One").Value,
Prop2 = node.Element("One").Attributes().Where(
a => A.Value.Contains("Foo").Value
};
If conditions are needed, then (extension) methods and arbitrary expressions can be used (null-coalescing operator, ??, can be very helpful to default).

Categories

Resources