I have XML:
<?xml version="1.0" encoding="UTF-8"?>
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
<gesmes:subject>Reference rates</gesmes:subject>
<gesmes:Sender>
<gesmes:name>European Central Bank</gesmes:name>
</gesmes:Sender>
<Cube>
<Cube time='2015-12-16'>
<Cube currency='USD' rate='1.0933'/>
<Cube currency='JPY' rate='133.18'/>
</Cube>
</Cube>
</gesmes:Envelope>
which I'm trying to deserialize with:
[XmlRoot("Envelope", Namespace = EcbNameSpace)]
public class EcbEnvelope
{
const string EcbNameSpace = "http://www.gesmes.org/xml/2002-08-01";
[XmlElement("Sender", Namespace = EcbNameSpace)]
public string EcbSender { get; set; }
[XmlElement("subject", Namespace = EcbNameSpace)]
public string EcbSubject { get; set; }
[XmlArray("Cube")]
[XmlArrayItem("Cube")]
public List<CubeRoot> CubeRootEl { get; set; }
public class CubeRoot
{
[XmlAttribute("time")]
public string Time { get; set; }
[XmlElement("Cube")]
public List<CubeItem> CubeItems { get; set; }
public class CubeItem
{
[XmlAttribute("rate")]
public string RateStr { get; set; }
[XmlIgnore]
public decimal Rate
{
get { return decimal.Parse(RateStr); }
}
[XmlAttribute("currency")]
public string Currency { get; set; }
}
}
}
However, it deserializes CubeRootEl as empty list. If I remove namspaces from XML, then it deserializes successfully. What am I doing wrong? I tried adding empty namespaces to CubeRootEl in code, but also unsuccessfully.
Here's the code used for deserialization:
var serializer = new XmlSerializer(typeof(EcbEnvelope));
using (var streamReader = new StreamReader(pathToFile))
var a = serializer.Deserialize(streamReader) as EcbEnvelope;
}
You have a few issues:
You do not specify a namespace for the property public List<CubeRoot> CubeRootEl. When a namespace for a property is not specified, XmlSerializer assumes it to be in the same namespace as the containing element, which would in this case be "http://www.gesmes.org/xml/2002-08-01". But the <Cube> elements are not in this namespace since they do not have the "gesmes:" prefix.
Your XML has a default namespace declaration xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref". Thus the <Cube> elements are actually in this namespace.
Sender is not a simple string-valued element, it has a nested element <gesmes:name>European Central Bank</gesmes:name>.
Fixing these three issues allows your XML to be deserialized:
[XmlRoot("Envelope", Namespace = GesmesNameSpace)]
public class EcbEnvelope
{
public const string GesmesNameSpace = "http://www.gesmes.org/xml/2002-08-01";
public const string EcbNameSpace = "http://www.ecb.int/vocabulary/2002-08-01/eurofxref";
[XmlElement("Sender", Namespace = GesmesNameSpace)]
public EcbSender Sender { get; set; }
[XmlElement("subject", Namespace = GesmesNameSpace)]
public string EcbSubject { get; set; }
[XmlArray("Cube", Namespace = EcbNameSpace)]
[XmlArrayItem("Cube")]
public List<CubeRoot> CubeRootEl { get; set; }
public class EcbSender
{
[XmlElement("name")]
public string Name { get; set; }
}
public class CubeRoot
{
[XmlAttribute("time")]
public string Time { get; set; }
[XmlElement("Cube")]
public List<CubeItem> CubeItems { get; set; }
public class CubeItem
{
[XmlAttribute("rate")]
public string RateStr { get; set; }
[XmlIgnore]
public decimal Rate
{
get { return decimal.Parse(RateStr); }
}
[XmlAttribute("currency")]
public string Currency { get; set; }
}
}
}
Related
I'm attempting to deserialize an XML file into corresponding C# objects. I've read through the other answers and I'm at a loss as to what I'm doing wrong.
Here's my XML file
<?xml version="1.0" encoding="utf-8"?>
<DialogueObjectCollection>
<DialogueObjects>
<DialogueObject id="0001">
<name>CHARACTER</name>
<dialogue>
<text tag="1">Hi, this is a message.</text>
<text tag="2">Yup.</text>
<text tag="3">What do you want to do?
<options>
<option action= "1">Go back.</option>
<option action="4">Tell me something new.</option>
</options>
</text>
<text tag= "4">This is the end.</text>
</dialogue>
</DialogueObject>
<DialogueObject id="0002">
<name>CHARACTER2</name>
<dialogue>
<text tag="1">Hi.</text>
</dialogue>
</DialogueObject>
</DialogueObjects>
</DialogueObjectCollection>
Here are my classes:
{
[Serializable(), XmlRoot("DialogueObject")]
public class DialogueObject
{
[XmlAttribute("id")]
public string id { get; set; }
[XmlElement("name")]
public string name { get; set; }
[XmlAttribute("tag")]
public int tag { get; set; }
public OptionHolder option;
public DialogueHolder dialogueHolder { get; set; }
[XmlAttribute("action")]
public string action { get; set; }
}
[Serializable(), XmlRoot("dialogue")]
public class DialogueHolder
{
[XmlArray("dialogue")]
[XmlArrayItem("text", IsNullable = false)]
public TextItem[] dialogue { get; set; }
}
[Serializable(),XmlRoot("text")]
public class TextItem
{
[XmlAttribute]
public string tag { get; set; }
public string text { get; set; }
}
[Serializable(),XmlRoot("option")]
public class OptionHolder
{
[XmlAttribute]
public string action;
[XmlElement("option")]
public string option;
}
[Serializable()]
[System.Xml.Serialization.XmlRoot("DialogueObjectCollection")]
public class DialogueObjectCollection
{
[XmlArray("DialogueObjects")]
[XmlArrayItem("DialogueObject", typeof(DialogueObject))]
public DialogueObject[] dialogueObject { get; set; }
}
And my method:
public static void LoadDialogue()
{
DialogueObjectCollection dialogueCollection = null;
string path = "Content/NPCdata.xml";
XmlSerializer serializer = new XmlSerializer(typeof(DialogueObjectCollection));
Console.WriteLine("LOADDINGGGG");
StreamReader reader = new StreamReader(path);
dialogueCollection = (DialogueObjectCollection)serializer.Deserialize(reader);
//test print Console.WriteLine(dialogueCollection.dialogueObject.First().dialogueHolder.dialogue.First().text);
}
So, it's telling me that dialogueHolder is returning null. I can get the dialogueObject.First().name and id to print. I can't figure out why the dialogue text isn't loading into it. (My attempts at fixing it included adding the XmlRootNode attributes and adding more classes-- I'm new to XML serialization in C#)
Thanks for any help!
try code below :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication3
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
LoadDialogue(FILENAME);
}
public static void LoadDialogue(string path)
{
XmlReader reader = XmlReader.Create(path);
XmlSerializer serializer = new XmlSerializer(typeof(DialogueObjectCollection));
DialogueObjectCollection dialogueObjectCollection = (DialogueObjectCollection)serializer.Deserialize(reader);
}
}
[Serializable(), XmlRoot("DialogueObject")]
public class DialogueObject
{
[XmlAttribute("id")]
public string id { get; set; }
[XmlElement("name")]
public string name { get; set; }
[XmlAttribute("tag")]
public int tag { get; set; }
public OptionHolder option;
[XmlElement("dialogue")]
public DialogueHolder dialogueHolder { get; set; }
[XmlAttribute("action")]
public string action { get; set; }
}
[Serializable(), XmlRoot("dialogue")]
public class DialogueHolder
{
[XmlElement("text")]
public TextItem[] texItem { get; set; }
}
[Serializable(), XmlRoot("text")]
public class TextItem
{
[XmlAttribute]
public string tag { get; set; }
[XmlText()]
public string text { get; set; }
[XmlArray("options")]
[XmlArrayItem("option")]
public OptionHolder[] options { get; set; }
}
[Serializable(), XmlRoot("option")]
public class OptionHolder
{
[XmlAttribute]
public string action;
[XmlElement("option")]
public string option;
}
[XmlRoot("DialogueObjectCollection")]
public class DialogueObjectCollection
{
[XmlArray("DialogueObjects")]
[XmlArrayItem ("DialogueObject")]
public DialogueObject[] dialogueObject { get; set; }
}
}
I wonder how to implement the method ReadXml of the IXmlSerializable interface when my XML contains recursive tags like in the following example:
<?xml version='1.0' encoding='utf-8'?>
<dform>
<label name='label-a' text='A' dbpath='module/label-a'/>
<textmemo name='textmemo-a' text='' dbpath='module/textmemo-a'/>
<section name='section-a' text='' dbpath='module/section-a'>
<textmemo name='textmemo-b' text='' dbpath='module/textmemo-b'/>
</section>
<section name='section-b' text='' dbpath='module/section-b'>
<textmemo name='textmemo-c' text='' dbpath='module/textmemo-c'/>
<label name='label-c' text='A' dbpath='module/label-c'/>
<section name='section-c' text='' dbpath='module/section-c'>
<label name='label-d' text='A' dbpath='module/label-d'/>
</section>
</section>
</dform>
The element <section> works like a container for all kind of elements, itself included.
I need to translate this structure into objects: a List of elements (objects) that contains other List objects when the element is a Section. So I've created the following Interface and Classes:
public interface IWidget
{
String type { get;}
String name { get; set; }
String labelCation { get; set; }
String text { get; set; }
String dbpath { get; set; }
}
class Textmemo : IWidget
{
public String type { get; }
public String name { get; set; }
public String labelCation { get; set; }
public String text { get; set; }
public String dbpath { get; set; }
public List<IWidget> subsection { get; set; }
public Textmemo()
{
type = "CONTROL";
}
}
class Label : IWidget
{
public String type { get; }
public String name { get; set; }
public String labelCation { get; set; }
public String text { get; set; }
public String dbpath { get; set; }
public List<IWidget> subsection { get; set; }
public Label()
{
type = "LABEL";
}
}
class Section : IWidget
{
public String type { get; }
public String name { get; set; }
public String labelCation { get; set; }
public String text { get; set; }
public String dbpath { get; set; }
public List<IWidget> subsection { get; set; }
public Section()
{
type = "SECTION";
subsection = new List<IWidget>();
}
}
The Section class has one more property compared with the interface IWidget from which it inherits, that is the subsection property.
Then I was ready to move my steps starting from this example XmlSerializer serialize generic List of interface
but I really don't understand how to manage the <section> element in my code, that, at the moment, it is nothing more than the signature of the class:
public class WidgetsList: List<IWidget>, IXmlSerializable
{
public WidgetsList() : base() { }
public System.Xml.Schema.XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader)
{
}
public void WriteXml(XmlWriter writer)
{
}
}
Thanks a lot for the advice!
Why are you wanting to implement IXmlSerializable yourself? You can use various attributes to have the framework do this for you. For example, define a base class (given all your implementations are the same):
public abstract class Widget
{
[XmlIgnore]
public abstract string type { get;}
[XmlAttribute]
public string name { get; set; }
[XmlIgnore]
public string labelCation { get; set; }
[XmlAttribute]
public string text { get; set; }
[XmlAttribute]
public string dbpath { get; set; }
}
The attributes here specify that type and labelCation are ignored (as they don't exist in the XML). The rest are mapped to XML attributes (e.g. name='abc').
You can then create your three sub-types:
public class Textmemo : Widget
{
public override string type { get; } = "CONTROL";
}
public class Label : Widget
{
public override string type { get; } = "LABEL";
}
public class Section : Widget
{
public override string type { get; } = "SECTION";
[XmlElement("textmemo", Type=typeof(Textmemo))]
[XmlElement("label", Type=typeof(Label))]
[XmlElement("section", Type=typeof(Section))]
public List<Widget> subsection { get; } = new List<Widget>();
}
Section is the interesting one as it defines the types permitted as child elements with the relevant name mappings. Similarly to this, you'd then define the root element:
[XmlRoot("dform")]
public class DForm
{
[XmlElement("textmemo", Type=typeof(Textmemo))]
[XmlElement("label", Type=typeof(Label))]
[XmlElement("section", Type=typeof(Section))]
public List<Widget> Widgets { get; } = new List<Widget>();
}
You can see in this demo that your XML round-tripped through the serializer is the same, thus proving this works.
Below I wrote parser using Xml Linq that will work
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
new Widget(FILENAME);
}
}
public class Widget
{
public static Widget root = new Widget();
public String type { get; set; }
public String name { get; set; }
public String labelCation { get; set; }
public String text { get; set; }
public String dbpath { get; set; }
public List<Widget> subsection { get; set; }
public Widget() { }
public Widget(string filename)
{
XDocument doc = XDocument.Load(filename);
XElement root = doc.Root;
RecursiveParse(root, Widget.root);
}
public static void RecursiveParse(XElement xParent, Widget textParent)
{
foreach (XElement child in xParent.Elements())
{
string elementName = child.Name.LocalName;
switch (elementName)
{
case "label" :
textParent.name = (string)child.Attribute("name");
textParent.labelCation = (string)child.Attribute("text");
textParent.dbpath = (string)child.Attribute("dbpath");
break;
case "section" :
if (textParent.subsection == null) textParent.subsection = new List<Widget>();
Widget childSection = new Widget();
textParent.subsection.Add(childSection);
RecursiveParse(child, childSection);
break;
default :
textParent.text = (string)child.Attribute("text");
textParent.labelCation = elementName;
break;
}
}
}
}
}
This is my first time to ask on stackoverflow and also the first time to work with xml files , so I don't think it can get worse than that.
I need to deserialize some long XML but the part thats bugging me is the following:
<CastleConfigSub xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../xsd/c5c.xsd" Format="1">
<ConfigFile Name="EdgeDetection">
<Interfaces>
<Interface Name="EdgeDetectionModule">
<Doc />
<Functions>
<Function Name="MonitorNoChanges">
<Doc>This Function checks that no edge has been detected at the specified digital channel for a specific time in msec
1. DigitalChanelToWatch: This is the digital Input channel to monitor edges on it.
2. TimeOut: This is the monitoring Period for the edges on the digitial input channel.
</Doc>
<Args>
<Arg xsi:type="ArgEnum" Name="DigitalChanelToWatch" Enum="DigitalInChannelID" />
<Arg xsi:type="ArgValue" Name="TimeOut" EncodedType="uint32" Unit="msec" />
</Args>
</Function>
</Functions>
</Interface>
</Interfaces>
</ConfigFile>
</CastleConfigSub>
public class CastleConfigSub
{
[XmlElement("Options")]
public Options options = new Options();
[XmlElement("ConfigFile")]
public ConfigFile configFile= new ConfigFile();
}
public class ConfigFile
{
[XmlElement("Doc")]
public string doc {get; set;}
[XmlElement("History")]
public History history = new History();
[XmlElement("Includes")]
public Includes includes = new Includes();
[XmlElement("Options")]
public Options options = new Options();
[XmlElement("DataTypes")]
public DataTypes dataTypes = new DataTypes();
[XmlArray("Interfaces")]
[XmlArrayItem("Interface")]
public List<Interface> interfaces = new List<Interface>();
}
public class Interface
{
[XmlAttribute("Name")]
public string name="";
[XmlElement("Doc")]
[XmlArray("Functions")]
[XmlArrayItem("Function")]
public List<Function> functions = new List<Function>();
}
public class Function
{
[XmlAttribute("Name")]
public string name="";
[XmlElement("Doc")]
public string doc="";
[XmlArray("Args")]
[XmlArrayItem("Arg")]
public List<Arg> args = new List<Arg>();
}
public class Arg
{
[XmlAttribute ("xsi:type")]
public string type = "";
[XmlAttribute("Name")]
public string name ="";
[XmlAttribute("EncodedType")]
public string encodedType="";
[XmlAttribute("Enum")]
public string enumName ="";
[XmlAttribute("Unit")]
public string unit="";
}
I know everthing is so messy but i couldnt do any better :/.
Please try this:
public class CastleConfigSub
{
public ConfigFile ConfigFile { get; set; }
[XmlAttribute()]
public byte Format { get; set; }
}
public class ConfigFile
{
public List<Interface> Interfaces { get; set; }
[XmlAttribute()]
public string Name { get; set; }
}
public class Interface
{
public object Doc { get; set; }
public List<Function> Functions { get; set; }
[XmlAttribute()]
public string Name { get; set; }
}
public class Function
{
public string Doc { get; set; }
[XmlArrayItem("Arg")]
public List<Arg> Args { get; set; }
[XmlAttribute()]
public string Name { get; set; }
}
[XmlInclude(typeof(ArgEnum))]
[XmlInclude(typeof(ArgValue))]
public class Arg
{
[XmlAttribute()]
public string Name { get; set; }
}
public class ArgEnum : Arg
{
[XmlAttribute()]
public string Enum { get; set; }
}
public class ArgValue : Arg
{
[XmlAttribute()]
public string EncodedType { get; set; }
[XmlAttribute()]
public string Unit { get; set; }
}
I do not know how many times Interface and Function elements exists. So I made the List collection.
The previous answer is a good one, and helped me to solve my issue, but I've got extra work to handle different namespaces in xml element and it's attribute value, so my solution is here:
Having following xml
<?xml version="1.0" encoding="utf-16"?>
<RootType xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://my.custom.namespace.com">
<description>some description</description>
<values>
<field>some field</field>
<value xsi:type="xsd:double">1000.00</value>
</values>
</RootType>
The deserialized objects set is following
[XmlRoot(ElementName = "RootType", Namespace = "http://my.custom.namespace.com")]
public sealed class Root
{
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("values")]
public Value[] Values { get; set; }
}
public sealed class Value
{
[XmlElement("field")]
public string Field { get; set; }
[XmlElement("value", IsNullable = true)]
public ValueProperty ValueProperty { get; set; }
}
[XmlInclude(typeof(CustomDouble))]
[XmlRoot(Namespace = "http://my.custom.namespace.com")]
public class ValueProperty
{
[XmlText]
public string Value { get; set; }
}
[XmlType(TypeName = "double", Namespace = "http://www.w3.org/2001/XMLSchema")]
public class CustomDouble : ValueProperty
{
}
Data ("1.0.0") from attribute "version" fall into property. But the property "Event" in the same class remains empty.
If cut namespace from XML, it will work.
My XML:
<rootnode>
<ns:eventresponse xmlns:ns="somenamespace" version="1.0.0">
<event id="694717028">
<somedata>val</somedata>
</event>
</ns:eventresponse>
</rootnode>
My class:
[XmlRoot("rootnode")]
public class RootNode
{
[XmlElement(ElementName = "eventresponse", Namespace = "somenamespace")]
public EventResponseData EventResponse { get; set; }
}
public class EventResponseData
{
[XmlElement("event")]
public EventData Event { get; set; }
[XmlAttribute("version")]
public string Version { get; set; }
}
public class TvEventData
{
[XmlAttribute("id")]
public string EventID { get; set; }
[XmlElement("somedata")]
public string SomeData { get; set; }
}
My Deserializer:
using (var reader = XmlReader.Create(new StringReader(xml)))
{
reader.MoveToContent();
var obj = (T)new XmlSerializer(typeof(T)).Deserialize(reader);
}
Your event element is implicitly in the same namespace in the XML due to namespace defaulting, so you should specify that in your declaration:
public class EventResponseData
{
[XmlElement(ElementName = "event", Namespace = "somenamespace")]
public EventData Event { get; set; }
[XmlAttribute("version")]
public string Version { get; set; }
}
Given the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<userAttributeList>
<attribute>
<userId>12345678</userId>
<attId>1234</attId>
<attName>Group</attName>
<attTypeId>8</attTypeId>
<attTypeName>User Group</attTypeName>
<attData>Member</attData>
</attribute>
<attribute>
<userId>12345678</userId>
<attId>1235</attId>
<attName>Contact Name</attName>
<attTypeId>16</attTypeId>
<attTypeName>Contact Center Greeting</attTypeName>
<attData>John Smith</attData>
</attribute>
...
</userAttributeList>
I want to deserialize it into the following classes:
[Serializable]
[XmlTypeAttribute(AnonymousType = true)]
public class UserAttributeList
{
[XmlArray(ElementName = "userAttributeList")]
[XmlArrayItem(ElementName = "attribute")]
public List<UserAttribute> attributes { get; set; }
public UserAttributeList()
{
attributes = new List<UserAttribute>();
}
}
[Serializable]
public class UserAttribute
{
public String userId { get; set; }
public String attId { get; set; }
public String attName { get; set; }
public String attTypeId { get; set; }
public String attTypeName { get; set; }
public String attData { get; set; }
}
Using the code below, where GetResponseStream() returns the XML object listed above:
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "userAttributeList";
xRoot.IsNullable = true;
XmlSerializer serializer = new XmlSerializer(typeof(UserAttributeList), xRoot);
try
{
return (UserAttributeList)serializer.Deserialize(request.GetResponse().GetResponseStream());
}
catch (Exception exc)
{
return null;
}
My code compiles with no errors, but the UserAttributeList that is returned shows no child "attribute" items. No errors are thrown
I would sooner do something like:
public class userAttributeList
{
[XmlElement]
public List<UserAttribute> attribute { get; set; }
public UserAttributeList()
{
attribute = new List<UserAttribute>();
}
}
public class UserAttribute
{
public int userId { get; set; }
public int attId { get; set; }
public string attName { get; set; }
public int attTypeId { get; set; }
public string attTypeName { get; set; }
public string attData { get; set; }
}
Personally I'd use LinqToXsd. Take the existing xml, generate an xsd from it then use LinqToXsd to load that xml into a LinqToXsd object. Then you can do things like:
xml.company.com.reports.report.Load(xmlFileContents);
to build a POCO.