XmlSerialization Collection as Array - c#

I'm trying to serialize a custom class that needs to use multiple elements of the same name.
I've tried using xmlarray, but it wraps them in another elements.
I want my xml to look like this.
<root>
<trees>some text</trees>
<trees>some more text</trees>
</root>
My code:
[Serializable(), XmlRoot("root")]
public class test
{
[XmlArray("trees")]
public ArrayList MyProp1 = new ArrayList();
public test()
{
MyProp1.Add("some text");
MyProp1.Add("some more text");
}
}

Try just using [XmlElement("trees")]:
[Serializable(), XmlRoot("root")]
public class test
{
[XmlElement("trees")]
public List<string> MyProp1 = new List<string>();
public test()
{
MyProp1.Add("some text");
MyProp1.Add("some more text");
}
}
Note I changed ArrayList to List<string> to clean up the output; in 1.1, StringCollection would be another option, although that has different case-sensitivity rules.

(edit: obsoleted - my second post ([XmlElement]) is the way to go - I'm leaving this for posterity in using xsd.exe)
xsd.exe is your friend. Copy the xml you want into a file (foo.xml), then use:
xsd foo.xml
xsd foo.xsd /classes
Now read foo.cs; you can use this either directly, or just for inspiration.
(edit: output snipped - not helpful any more)

Related

Serialization xml with double xml tags where order is important in c#

I want to serialize xml which works but I have a problem. Sometimes I have the same tag multiple times but on different places. So the order is important so I cant just stick it in an list.
For example I have the following xml:
<?xml version="1.0" encoding="utf-16"?>
<commands>
<execute>some statement</execute>
<wait>5</wait>
<execute>some statement</execute>
<wait>5</wait>
<execute>some statement</execute>
<execute>some statement</execute>
</commands>
Then my object would look something like this:
[XmlRoot(ElementName="commands")]
public class Commands {
[XmlElement(ElementName="execute")]
public List<string> Execute { get; set; }
[XmlElement(ElementName="wait")]
public List<int> Wait { get; set; }
}
If I then serialize it with the following function:
var xmlSerializer = new XmlSerializer(obj.GetType());
using (var writer = new Utf8StringWriter())
{
xmlSerializer.Serialize(writer, obj);
return writer.ToString();
}
The order will not be the same.... It would first serialize the execute tags and then the wait statements. While the order is important.
Does someone have a clue on how to tackle this problem?
Ps. changing the xml is not a solution as I'm tied to that....
Thanks in advance!
After searching for a while I tackle the problem as the following. The wait and execute are basically commands to I serialize now a list of commands. Of course the seriliazer will complain that it can't serlize this because it is a list of interfaces (ICommand) so I implemented the IXmlSerliazer so that I tell how to serliaze this.
This worked actually quite good

Classes generated from XSD does not work with XmlSerializer

I need to read some XML files that follow the ONIX standard
See: http://www.editeur.org/93/Release-3.0-Downloads/
To do this i downloaded the ONIX 3.0 XSD:
http://www.editeur.org/files/ONIX%203/ONIX_BookProduct_XSD_schema+codes_Issue_25.zip
Using the downloaded XSD and this command "xsd your.xsd /classes" i created classes that i want to use.
When trying to create a new Xml Serializer like so:
var xmls = new XmlSerializer(typeof(Model.ONIX.editeur.ONIXMessage));
I get and exception
"There was an error reflecting type 'Model.ONIX.editeur.ONIXMessage'."
When i drill down through the inner exceptions i end up with this message:
"{"Member 'Text' cannot be encoded using the XmlText attribute. You
may use the XmlText attribute to encode primitives, enumerations,
arrays of strings, or arrays of XmlNode."}"
I am not sure what to do, is something wrong with the XSD? Any suggestions?!
Edit
public static List<Model.ONIX.editeur.Product> GetProductsDataFromOnixFile(string onixFileLocation)
{
var xmls = new XmlSerializer(typeof(Model.ONIX.editeur.ONIXMessageRefname));
using (var reader = XmlReader.Create(onixFileLocation))
{
if (xmls.CanDeserialize(reader))
{
var onixMessage = (Model.ONIX.editeur.ONIXMessage)xmls.Deserialize(reader);
return onixMessage.Items.OfType<Model.ONIX.editeur.Product>().ToList();
}
throw new Exception(string.Format("Cant read the file {0} as Onix", onixFileLocation));
}
}
I know this question is old but I assume others with specific Onix issues will run into this.
Here is how I got it to work.
In the reference xsd are two includes in the top. Here I copy/pasted the other two files in.
<xs:include schemaLocation="ONIX_BookProduct_CodeLists.xsd" />
<xs:include schemaLocation="ONIX_XHTML_Subset.xsd" />
I.e. these lines are replaced in the file with the corresponding file.
Then I did the
xsd ONIX_BookProduct_3.0_reference.xsd /classes
And then it generates the .cs file. And the only issue I had here was I had to remove a text attribute from all fields that was e.g. List147, but not from the fields that was string. E.g. I had to remove the attribute from generated code like this:
/// <remarks/>
[System.Xml.Serialization.XmlTextAttribute()]
public List121 textscript {
get {
return this.textscriptField;
but not from attributes like this
/// <remarks/>
[System.Xml.Serialization.XmlTextAttribute()]
public string Value {
get {
return this.valueField;

Rss20FeedFormatter and RRS2 extensions

I want to use the RSS2 extensions feature to add my own non-standard elements to my RSS feed as described here:
http://cyber.law.harvard.edu/rss/rss.html#extendingRss:
However I don't think that the .Net Rss20FeedFormatter class supports this feature.
My code looks something like this:
public Rss20FeedFormatter GetRSS()
{
var feed = new SyndicationFeed(....);
feed.Items = new List<SyndicationItem>();
// add items to feed
return new Rss20FeedFormatter(feed);
}
If it doesn't support it is there any alternative to just creating the XML element by element?
Here's my findings. Took me a while to figure it all out.
This is what you do, your feed has to have a namespace
XNamespace extxmlns = "http://www.yoursite.com/someurl";
feed.AttributeExtensions.Add(new XmlQualifiedName("ext", XNamespace.Xmlns.NamespaceName), extxmlns.NamespaceName);
feed.ElementExtensions.Add(new XElement(extxmlns + "link", new XAttribute("rel", "self"), new XAttribute("type", "application/rss+xml")));
return new Rss20FeedFormatter(feed, false);
Your items need to be a derived class, and you write the extended properties in WriteElementExtensions, making sure you prefix them with the namespace (you don't have to but this is what is required to make it valid RSS).
class TNSyndicationItem : SyndicationItem
protected override void WriteElementExtensions(XmlWriter writer, string version)
{
writer.WriteElementString("ext:abstract", this.Abstract);
writer.WriteElementString("ext:channel", this.Channel);
}
The extended properties are ignore if you look in an RSS reader such as firefox, you'll need to write code to read them as well.
The url http://www.yoursite.com/someurl doesn't have to exist, but you need it to define the namespace and make the RSS valid. Normally you'll just put a page there which says something about what the feed should look like.

How do I use XmlSerializer to handle different namespace versions?

I am using the .NET XmlSerializer class to deserialize GPX files.
There are two versions of the GPX standard:
<gpx xmlns="http://www.topografix.com/GPX/1/0"> ... </gpx>
<gpx xmlns="http://www.topografix.com/GPX/1/1"> ... </gpx>
Also, some GPX files do not specify a default namespace:
<gpx> ... </gpx>
My code needs to handle all three cases, but I can't work out how to get XmlSerializer to do it.
I am sure there must be a simple solution because this a common scenario, for example KML has the same issue.
I have done something similar to this a few times before, and this might be of use to you if you only have to deal with a small number of namespaces and you know them all beforehand. Create a simple inheritance hierarchy of classes, and add attributes to the different classes for the different namespaces. See the following code sample. If you run this program it gives the output:
Deserialized, type=XmlSerializerExample.GpxV1, data=1
Deserialized, type=XmlSerializerExample.GpxV2, data=2
Deserialized, type=XmlSerializerExample.Gpx, data=3
Here is the code:
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
[XmlRoot("gpx")]
public class Gpx {
[XmlElement("data")] public int Data;
}
[XmlRoot("gpx", Namespace = "http://www.topografix.com/GPX/1/0")]
public class GpxV1 : Gpx {}
[XmlRoot("gpx", Namespace = "http://www.topografix.com/GPX/1/1")]
public class GpxV2 : Gpx {}
internal class Program {
private static void Main() {
var xmlExamples = new[] {
"<gpx xmlns='http://www.topografix.com/GPX/1/0'><data>1</data></gpx>",
"<gpx xmlns='http://www.topografix.com/GPX/1/1'><data>2</data></gpx>",
"<gpx><data>3</data></gpx>",
};
var serializers = new[] {
new XmlSerializer(typeof (Gpx)),
new XmlSerializer(typeof (GpxV1)),
new XmlSerializer(typeof (GpxV2)),
};
foreach (var xml in xmlExamples) {
var textReader = new StringReader(xml);
var xmlReader = XmlReader.Create(textReader);
foreach (var serializer in serializers) {
if (serializer.CanDeserialize(xmlReader)) {
var gpx = (Gpx)serializer.Deserialize(xmlReader);
Console.WriteLine("Deserialized, type={0}, data={1}", gpx.GetType(), gpx.Data);
}
}
}
}
}
Here's the solution I came up with before the other suggestions came through:
var settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreWhitespace = true;
using (var reader = XmlReader.Create(filePath, settings))
{
if (reader.IsStartElement("gpx"))
{
string defaultNamespace = reader["xmlns"];
XmlSerializer serializer = new XmlSerializer(typeof(Gpx), defaultNamespace);
gpx = (Gpx)serializer.Deserialize(reader);
}
}
This example accepts any namespace, but you could easily make it filter for a specific list of known namespaces.
Oddly enough you can't solve this nicely. Have a look at the deserialize section in this troubleshooting article. Especially where it states:
Only a few error conditions lead to exceptions during the
deserialization process. The most common ones are:
•The name of the
root element or its namespace did not match the expected name.
...
The workaround I use for this is to set the first namespace, try/catch the deserialize operation and if it fails because of the namespace I try it with the next one. Only if all namespace options fail do I throw the error.
From a really strict point of view you can argue that this behavior is correct since the type you deserialize to should represent a specific schema/namespace and then it doesn't make sense that it should also be able to read data from another schema/namespace. In practice this is utterly annoying though. File extenstion rarely change when versions change so the only way to tell if a .gpx file is v0 or v1 is to read the xml contents but the xmldeserializer won't unless you tell upfront which version it will be.

Xml Attribute not showing up on ListBox - C#

I'm getting a null reference exception whenever it tries to add the packages titles info and other attributes but the attributes exist and the proper package is selected
Heres the code:
private void categorylist_listview_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
XmlDocument LoadPackageList = new XmlDocument();
//Removes the text "Select A Category" and refrehes the form
packagelist_listbox.Items.Remove(SelectaCategory_listbox);
if (categorylist_listview.SelectedItem == WWW_listviewitem)
{
LoadPackageList.Load("www.xml");
XmlNodeList WWWPackageList = LoadPackageList.SelectNodes("/Packages/*");
int countthenodes = 0;
foreach (XmlNode WWWPackages in WWWPackageList)
{
//Cycles through all the packages and assings them to a string then adds it to the packagelist
countthenodes++;
PackageTitle[countthenodes] = WWWPackages.Attributes["title"].ToString();
PackageInfo[countthenodes] = WWWPackages.Attributes["info"].ToString();
PackageDownloadUrl[countthenodes] = WWWPackages.Attributes["downloadurl"].ToString();
PackageTags[countthenodes] = WWWPackages.Attributes["tags"].ToString();
packagelist_listbox.Items.Add(PackageTitle[countthenodes]);
}
Refresh(packagelist_listbox);
}
}
It Errors out at PackageTitle[countthenodes] = WWWPackages.Attributes["title"].ToString();
XML File:
<Packages>
<Firefox title="Mozilla Firefox" tags="www firefox web browser mozilla" info="http://google.com" downloadurl="http://firefox.com"></Firefox>
</Packages>
The Variables are declared
public string[] PackageTags;
public string[] PackageTitle;
public string[] PackageInfo;
public string[] PackageDownloadUrl;
At the very beginning of the file
Well, the first problem is that calling ToString() on an XmlAttribute isn't going to do what you want. You should use the Value property. However, I don't believe that's causing a NullReferenceException unless the data isn't quite as you showed it. Here's a short but complete program which works fine:
using System;
using System.Xml;
class Test
{
static void Main()
{
XmlDocument doc = new XmlDocument();
doc.Load("test.xml");
XmlNodeList list = doc.SelectNodes("/Packages/*");
foreach (XmlNode node in list)
{
Console.WriteLine(node.Attributes["title"].Value);
}
}
}
That displays "Mozilla Firefox" with the XML you gave us.
Options:
Your real XML actually contains an element without a title attribute
Perhaps PackageTitle is null?
It would help if you could produce a short but complete program demonstrating the problem. Ideally it should avoid using a GUI - I can't see anything here which is likely to be GUI-specific.
If you could tell us more about PackageTitle and how it's being initialized, that would help too. How are you expecting it to just keep expanding for as many elements as you find? Or is it an array which is initialized to a larger size than you ever expect to find elements?

Categories

Resources