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
{
}
Related
There is a problem, that the object fields are initialized as null.
I've checked a couple of examples, I've set the field annotations, but seems like I did something wrong.
So here is my xml file:
<?xml version="1.0" encoding="UTF-8"?>
<getInvoiceReply>
<invoiceID value="944659502"/>
<invFastener>
<fastenerID value=""/>
<fastenerName value=""/>
<fastenerCount value=""/>
<fastenerProperty>
<propID value=""/>
<propName value=""/>
<propValue value=""/>
</fastenerProperty>
</invFastener>
</getInvoiceReply>
I've created the class hierarcy.
Root class InvoiceReply :
[XmlRoot("getInvoiceReply")]
public class InvoiceReply
{
[XmlAttribute("invoiceID")]
public string InvoiceId { get; set; }
[XmlArray("invFastener")]
public List<InvFastener> InvFastener { get; set; }
}
class InvFastener :
public class InvFastener
{
[XmlAttribute("fastenerID")]
public string FastenerID { get; set; }
[XmlAttribute("fastenerName")]
public string FastenerName { get; set; }
[XmlAttribute("fastenerCount")]
public string FastenerCount { get; set; }
[XmlArray("fastenerProperty")]
public List<FastenerProperty> FastenerProperty { get; set; }
}
class FastenerProperty:
public class FastenerProperty
{
[XmlAttribute("propID")]
public string PropId { get; set; }
[XmlAttribute("propName")]
public string PropName { get; set; }
[XmlAttribute("propValue")]
public string PropValue { get; set; }
}
Test code:
InvoiceReply i = null;
var serializer = new XmlSerializer(typeof(InvoiceReply));
using (var reader = XmlReader.Create("C:\\filePathHere\\test.xml"))
{
i = (InvoiceReply)serializer.Deserialize(reader);
}
Could anyone please suggest why is this happens?
You have a few issues with your objects. You are trying to get attributes in place of elements and your arrays are not arrays, they are merely complex elements. Below is a working example that matches your xml schema
class Program
{
static void Main(string[] args)
{
string xml = #"<?xml version=""1.0"" encoding=""UTF-8""?>
<getInvoiceReply>
<invoiceID value=""944659502""/>
<invFastener>
<fastenerID value=""""/>
<fastenerName value=""""/>
<fastenerCount value=""""/>
<fastenerProperty>
<propID value=""""/>
<propName value=""""/>
<propValue value=""""/>
</fastenerProperty>
</invFastener>
</getInvoiceReply>";
var serializer = new XmlSerializer(typeof(InvoiceReply));
var i = (InvoiceReply)serializer.Deserialize(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(xml)));
Console.ReadKey();
}
}
//Generic class for getting value attribute
public class ValueElement
{
[XmlAttribute("value")]
public string Value { get; set; }
}
[XmlRoot("getInvoiceReply")]
public class InvoiceReply
{
[XmlElement("invoiceID")]
public ValueElement InvoiceId { get; set; } //This is a value element
[XmlElement("invFastener")]
public List<InvFastener> InvFastener { get; set; } //This is an element, not an array
}
public class InvFastener
{
[XmlElement("fastenerID")]
public ValueElement FastenerID { get; set; }//This is a value element
[XmlElement("fastenerName")]
public ValueElement FastenerName { get; set; }//This is a value element
[XmlElement("fastenerCount")]
public ValueElement FastenerCount { get; set; }//This is a value element
[XmlElement("fastenerProperty")]
public List<FastenerProperty> FastenerProperties { get; set; } //This is an element, not an array
}
public class FastenerProperty
{
[XmlElement("propID")]
public ValueElement PropId { get; set; }//This is a value element
[XmlElement("propName")]
public ValueElement PropName { get; set; }//This is a value element
[XmlElement("propValue")]
public ValueElement PropValue { get; set; }//This is a value element
}
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;
}
}
}
}
}
I am using XML for first time and a config file.
I am trying to read this config file in order to connect to different connection strings.
<?xml version="1.0"?>
<Config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<BDD id="aaa">
<Connexion>
<MetaDatas>
<Data id="SOC">Model</Data>
</MetaDatas>
<Provider>
<Name>System.Data.SqlClient</Name>
<DataSource>10.198.164.169</DataSource>
<Catalog>xxx</Catalog>
<User>sa</User>
<Password>xxx</Password>
</Provider>
</Connexion>
</BDD>
<BDD id="bbb">
<Connexion>
<MetaDatas>
<Data id= "SE">eee</Data>
<Data id="AD">fff</Data>
<Data id="FR">ggg</Data>
<Data id="DOC">hhh</Data>
</MetaDatas>
<Provider>
<Name>System.Data.SqlClient</Name>
<DataSource>10.198.164.169</DataSource>
<Catalog>yyy</Catalog>
<User>sa</User>
<Password>xx</Password>
</Provider>
</Connexion>
</BDD>
</Config>
with this following code structure,
[XmlRoot("Config")]
public class XmlConfigBDD : XmlFileConfiguration.IXmlConfiguration
{
[XmlArray("Config")]
[XmlArrayItem(typeof(DataBase), ElementName="BDD")]
public List<DataBase> BDD { get; set; }
public XmlFileConfiguration.IXmlConfiguration Default
{
get { return new XmlConfigBDD(); }
}
public XmlConfigBDD()
{
this.BDD = new List<DataBase>();
}
public string Path
{
get { return SE.Datas.Common.AppPaths.GetBddConfigurationFilePath(); }
}
}
public class DataBase
{
[XmlAttribute("id")]
public string Id { get; set; }
[XmlElement("Connexion")]
public ConfigConnection Connexion { get; set; }
public DataBase()
{
this.Connexion = new ConfigConnection();
}
}
public class ConfigConnection
{
[XmlArray("MetaDatas")]
//public ConfigMetaDatas MetaDatas { get; set; }
public List<ConfigMetaDatas> MetaDatas { get; set; }
[XmlElement("Provider")]
public ConfigProvider Provider { get; set; }
protected static readonly string _Connection_ = #"metadata=res://*/%Metadata%.csdl|res://*/%Metadata%.ssdl|res://*/%Metadata%.msl;provider=%Provider%;provider connection string=""%ProviderConnectionString%""";
public ConfigConnection()
{
this.MetaDatas = new List<ConfigMetaDatas>();
this.Provider = new ConfigProvider();
}
protected static readonly string _Metadata_TobeReplace_ = "%Metadata%";
protected static readonly string _Provider_TobeReplace_ = "%Provider%";
protected static readonly string _ProviderString_TobeReplace_ = "%ProviderConnectionString%";
public string getModelConnection()
{
return this.getConnectionString(this.MetaDatas.Find(ctx => ctx.Id == "SOC").Value);
}
/*...*/
}
public class ConfigMetaDatas
{
[XmlAttribute("id")]
public string Id { get; set; }
[XmlText(Type = typeof(string))]
public string Value { get; set; }
public ConfigMetaDatas()
{
}
}
public class ConfigProvider
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("DataSource")]
public string DataSource { get; set; }
[XmlElement("Catalog")]
public string Catalog { get; set; }
[XmlElement("User", IsNullable = true)]
public string User { get; set; }
[XmlElement("Password", IsNullable = true)]
public string Password { get; set; }
public ConfigProvider()
{
}
/*...*/
}
It doesn't work, I have tried different combinations.
The connection string returned is null.
Replace:
[XmlArray("Config")]
[XmlArrayItem(typeof(DataBase), ElementName="BDD")]
public List<DataBase> BDD { get; set; }
with:
[XmlElement]
public List<DataBase> BDD { get; set; }
(the original version indicates that there should be an extra <Config> inside the root <Config>)
And because the meta-data data does have a parent/child xml structure, it should be:
[XmlArray("MetaDatas")]
[XmlArrayItem("Data")]
public List<ConfigMetaDatas> MetaDatas { get; set; }
this is my first post here and as can you can imagine, i'm just a beginner in c#. So here's my question:
I'm deserializing a given xml to a custom class with XmlSerializer.
XML:
<epg>
<program start="20160404234500" stop="20160405001000" channel="281479340566592">
<eventid>604042345</eventid>
<titles>
<title>Schlauberger - Quizzen, was Spaß macht! (1)</title>
</titles>
<events>
<event>27. Schlauberger - Quizzen, was Spaß macht!</event>
</events>
<descriptions>
<description>27. Schlauberger - Quizzen, was Spaß macht!</description>
</descriptions>
</program>
<program start="20160504234500" stop="20160505001000" channel="281479340566587">
<eventid>604042348</eventid>
<title>Schlauberger - Quizzen, was Spaß macht! (2)</title>
<event>28. Schlauberger - Quizzen, was Spaß macht!</event>
<description>28. Schlauberger - Quizzen, was Spaß macht!</description>
</program>
Custom C# class:
public class Titles
{
[XmlElement("title")]
public string Title { get; set; }
}
public class SubTitles
{
[XmlElement("event")]
public string SubTitle { get; set; }
}
public class Descriptions
{
[XmlElement("description")]
public string Description { get; set; }
}
public class Program
{
[XmlElement("eventid")]
public string EventID { get; set; }
[XmlElement("titles")]
public Titles Titles { get; set; }
[XmlElement("events")]
public SubTitles SubTitles { get; set; }
[XmlElement("descriptions")]
public Descriptions Descriptions { get; set; }
[XmlAttribute("start")]
public string Start { get; set; }
[XmlAttribute("stop")]
public string Stop { get; set; }
[XmlAttribute("channel")]
public string Channel { get; set; }
[XmlElement("title")]
public string Title { get; set; }
[XmlElement("event")]
public string SubTitle { get; set; }
[XmlElement("description")]
public string Description { get; set; }
}
[XmlRoot(ElementName = "epg"), XmlType("epg")]
public class epg
{
[XmlElement("program")]
public List<Program> Program { get; set; }
}
No problem so far. But as you can see, because "title", "event" and "description" is sometimes nested in a majority and sometimes not, i can access the properties in my later code sometimes by a list and sometimes directly. This makes the code really inconvenient.
So is it somehow possible to ignore the majorities and always use the single nodes instead?
You could add some helper functions (or properties) to your Program class, e.g.:
public class Program
{
public Titles Titles { get; set; }
public string Title { get; set; }
public string GetTitle()
{
if (Titles != null)
{
return Titles.Title;
}
else
{
return Title;
}
}
}
I have the following code but unable to deserialize, can you see where I'm going wrong? It only catch the first record on the first array item.
[XmlRootAttribute("Booking")]
public class Reservation
{
[XmlArray("Included")]
[XmlArrayItem("Meals")]
public Meals[] Food { get; set; }
[XmlArrayItem("Drinks")]
public Drinks[] Drink { get; set; }
}
public class Meals
{
[XmlAttribute("Breakfast")]
public string Breakfast { get; set; }
[XmlAttribute("Lunch")]
public string Lunch { get; set; }
[XmlAttribute("Dinner")]
public string Dinner { get; set; }
}
public class Drinks
{
[XmlAttribute("Soft")]
public string Softs { get; set; }
[XmlAttribute("Beer")]
public string Beer { get; set; }
[XmlAttribute("Wine")]
public string Wine { get; set; }
}
Here's the associated XML
<?xml version="1.0" standalone="yes"?>
<Booking>
<Included>
<Meals
Breakfast="True"
Lunch="True"
Dinner="False">
</Meals>
<Drinks
Soft="True"
Beer="False"
Wine="False">
</Drinks>
</Included>
<Included>
<Meals
Breakfast="True"
Lunch="False"
Dinner="False">
</Meals>
<Drinks
Soft="True"
Beer="True"
Wine="True">
</Drinks>
</Included>
</Booking>
I'm a bit of a newbie so any help would be great, unfortunately after trawling through the many exmaples you already have online I still haven't been able to figure this out.
Use the following example and apply this syntax in ListItem array,
[XmlType("device_list")]
[Serializable]
public class DeviceList {
[XmlAttribute]
public string type { get; set; }
[XmlElement( "item" )]
public ListItem[] items { get; set; }
}
following link contains all the syntax & attributes
http://msdn.microsoft.com/en-us/library/2baksw0z.aspx
I see no obvious way your class structure could be matched to the XML document. The underlying organizations seem to be quite different.
The following class hierarchy could be easily deserialized from the XML document you provide (assuming your document covers the general case) :
[Serializable]
[XmlRoot("Booking")]
public class Booking : List<Included>
{
}
[Serializable]
public class Included
{
public Meals Meals { get; set; }
public Drinks Drinks { get; set; }
}
public class Meals
{
[XmlAttribute("Breakfast")]
public string Breakfast { get; set; }
[XmlAttribute("Lunch")]
public string Lunch { get; set; }
[XmlAttribute("Dinner")]
public string Dinner { get; set; }
}
public class Drinks
{
[XmlAttribute("Soft")]
public string Softs { get; set; }
[XmlAttribute("Beer")]
public string Beer { get; set; }
[XmlAttribute("Wine")]
public string Wine { get; set; }
}
Then, deserialization code would be : (serializedObject is the string containing your serialized object)
XmlSerializer ser = new XmlSerializer(typeof (string));
XmlReader reader = XmlTextReader.Create(new StringReader(serializedObject));
var myBooking = ser.Deserialize(reader) as Booking;