I am trying to define C# objects based on this XML:
<UPDs LUPD="86">
<UPD ID="106">
<ER R="CREn">
<NU UID="1928456" />
<NU UID="1886294" />
<M>
<uN>bob ยท </uN>
<mO>fine :D</mO>
</M>
So far I have:
public class UDPCollection
{
List<UDP> UDPs;
public UDPCollection()
{
UDPs = new List<UDP>();
}
}
public class UDP
{
public int Id;
public List<ER> ERs;
public UDP(int id, List<ER> ers)
{
Id = id;
ERs = ers;
}
}
public class ER
{
public string LanguageR;
public ER(string languager)
{
LanguageR = languager;
}
}
My questions: What do elements map to in C#? Classes? What do attributes map to? Properties? Am I going about this the correct way?
Use the XmlSerializer class and the XmlRoot, XmlElement and XmlAttribute attributes. For example:
using System.Xml.Serialization;
...
[XmlRoot("UPDs")]
public class UDPCollection
{
// XmlSerializer cannot serialize List. Changed to array.
[XmlElement("UPD")]
public UDP[] UDPs { get; set; }
[XmlAttribute("LUPD")]
public int LUPD { get; set; }
public UDPCollection()
{
// Do nothing
}
}
[XmlRoot("UPD")]
public class UDP
{
[XmlAttribute("ID")]
public int Id { get; set; }
[XmlElement("ER")]
public ER[] ERs { get; set; }
// Need a parameterless or default constructor.
// for serialization. Other constructors are
// unaffected.
public UDP()
{
}
// Rest of class
}
[XmlRoot("ER")]
public class ER
{
[XmlAttribute("R")]
public string LanguageR { get; set; }
// Need a parameterless or default constructor.
// for serialization. Other constructors are
// unaffected.
public ER()
{
}
// Rest of class
}
The code to write out the XML is:
using System.Xml.Serialization;
...
// Output the XML to this stream
Stream stream;
// Create a test object
UDPCollection udpCollection = new UDPCollection();
udpCollection.LUPD = 86;
udpCollection.UDPs = new []
{
new UDP() { Id= 106, ERs = new [] { new ER() { LanguageR = "CREn" }}}
};
// Serialize the object
XmlSerializer xmlSerializer = new XmlSerializer(typeof(UDPCollection));
xmlSerializer.Serialize(stream, udpCollection);
Not that the XmlSerializer adds additional namepsaces but it can parse XML without them if needed. The output of the above is:
<?xml version="1.0"?>
<UPDs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" LUPD="86">
<UPD ID="106">
<ER R="CREn" />
</UPD>
</UPDs>
Use the Deserialize() method to parse it from XML into an object.
XML elements and attributes don't necessarily map to anything in particular in C#. You can make them map to classes and properties if you want, but it's not required.
That said, if you want to map your existing XML to some sort of C# data structures, the way you're doing it seems reasonable - I'd just recommend replacing your public fields with actual properties, and maybe making the list properties a less specific type - say, IEnumerable, or ICollection, or IList if they really need to be in order.
Related
How to represent the structure of the following XML for its further deserialization into classes?
<HeadElement id="1">
Text in HeadElement start
<SubElement samp="0">
Text in SubElement
</SubElement>
Continue text
</HeadElement>
My current code looks like this:
[DataContract]
public class ClaimText
{
[DataMember, XmlElement(ElementName = "claim-ref")]
public ClaimRef claimref; // { get; private set; }
public void setclaimref(ClaimRef claimref_)
{
this.claimref = claimref_;
}
[DataMember, XmlText()]
public string txt; // { get; private set; }
public void settxt(string txt_)
{
this.txt = txt_;
}
}
Given the following XML contents:
<claim id="CLM-00016" num="00016">
<claim-text>16. The midgate assembly of <claim-ref idref="CLM-00015">claim 15</claim-ref>, further comprising a second ramp member connected to an opposite side of the midgate panel for selectively covering an opposite end of the pass-through aperture. </claim-text>
</claim>
I get the object in which the link to the "claim-ref" is present, but not the entire text: only the second part of it (", further comprising ..."). How to get the whole text?
First of all you are mixing attributes for DataContractSerializer and XmlSerializer.
Ad rem: in case of mixed elements it's better to use XmlSerializer. Here is the structure that works with your XML:
[XmlRoot(ElementName = "claim")]
public class ClaimText
{
[XmlAttribute]
public string id;
[XmlAttribute]
public string num;
[XmlElement(ElementName = "claim-text")]
public ClaimInnerContents contents;
}
public class ClaimInnerContents
{
[XmlElement(ElementName = "claim-ref", Type = typeof(ClaimRef))]
[XmlText(Type = typeof(string))]
public object[] contents;
}
public class ClaimRef
{
[XmlAttribute]
public string idref;
[XmlText]
public string text;
}
ClaimRef and splitted text sections are deserialized into contents array as objects.
You can (and should) of course provide statically typed and checked accessors to particular elements of this array.
EDIT
I made a mistake in my assumptions. I've answer myself to explain what was wrong and what I really need.
I have a problem to serialize a list of different custom classes. Each of them inherit from the same base class, and the Xml file should show elements with base class name.
This is how I want my Xml file looks like:
<TestDataMap>
<DataMap>
<Item Key="BoolStatus">True</Item>
<Item Key="IntStatus">77</Item>
</DataMap>
</TestDataMap>
My actual code is composed by a base class:
public class BaseItem
{
[XmlAttribute("Key")]
public string Key { get; set; }
[XmlText]
public string Value { get; set; }
}
and multiple custom classes, for example:
[XmlType("ItemBool")]
public class ItemBool : BaseItem
{
public ItemBool()
{
base.Key = string.Empty;
ValueBool = false;
}
[XmlIgnore]
public bool ValueBool
{
get { return bool.Parse(base.Value); }
set { base.Value = value.ToString(); }
}
}
[XmlType("ItemInt")]
public class ItemInt : BaseItem
{
public ItemInt()
{
base.Key = string.Empty;
ValueInt = int.MinValue;
}
[XmlIgnore]
public int ValueInt
{
get { return int.Parse(base.Value); }
set { base.Value = value.ToString(); }
}
}
I need to serialize all of these custom classes as a list of my base class, so I've done the following:
[XmlRoot("TestDataMap")]
public class TestDataMap
{
public TestDataMap()
{
DataMap = new List<BaseItem>();
}
// If I use the following I've got serialization exception...
//[XmlArrayItem("Item", typeof(ItemBool))]
//[XmlArrayItem("Item", typeof(ItemInt))]
[XmlArray("DataMap")]
[XmlArrayItem("Item", typeof(BaseItem))]
public List<BaseItem> DataMap { get; set; }
}
To test the serialization I'm using a button event:
private void button1_Click(object sender, EventArgs e)
{
TestDataMap testDataMap = new TestDataMap();
ItemBool itemBool = new ItemBool() { Key = "BoolStatus", ValueBool = true };
ItemInt itemInt = new ItemInt() { Key = "IntStatus", ValueInt = 77 };
testDataMap.DataMap.Add(itemBool);
testDataMap.DataMap.Add(itemInt);
TextWriter writer = new StreamWriter(#"c:\TempTest.xml");
Type[] dataMapTypes = new Type[] { typeof(ItemBool), typeof(ItemInt) };
XmlSerializer serializer = new XmlSerializer(typeof(TestDataMap), dataMapTypes);
serializer.Serialize(writer, testDataMap);
writer.Close();
}
But I obtain an output like this:
<TestDataMap>
<DataMap>
<Item Key="BoolStatus" xsi:type="ItemBool">True</Item>
<Item Key="IntStatus" xsi:type="ItemInt">77</Item>
</DataMap>
</TestDataMap>
that contains xsi:type information that I don't want to show...
What's wrong with my code? Any suggestions about what I have to change to obtain exactly the Xml structure I posted at the begin of the question?
When I asked this question I was too focus on the aesthetical result of the output xml file, instead of caring about the important point: xml serialization of custom classes and subsequent deserialization.
Even if I will be able to obtain exactly the xml elements as I originally posted, then I will have problems during deserialization: how can the deserializer knows, from a list of generic <Item> elements, which one is <ItemBool> and which one is <ItemInt>, without any other detail?
Thinking about this, I understand that what I've asked is not what I need, nor is the correct way to do xml serialization.
So I've changed the TestDataMap class I posted above to the following:
[XmlRoot("TestDataMap")]
public class TestDataMap
{
public TestDataMap()
{
DataMap = new List<BaseItem>();
}
[XmlArray("DataMap")]
[XmlArrayItem("ItemBool", typeof("ItemBool"))]
[XmlArrayItem("ItemInt", typeof("ItemInt"))]
public List<BaseItem> DataMap { get; set; }
}
To obtain this xml output:
<TestDataMap>
<DataMap>
<ItemBool Key="BoolStatus">True</ItemBool>
<ItemInt Key="IntStatus">77</ItemInt>
</DataMap>
</TestDataMap>
In this way, it's clear for the deserializer how it has to deserialize each type of <Item> element.
I need deserialize this XML structure :
<?xml version="1.0" encoding="iso-8859-1" ?>
<vspaccess>
<vpstxid>A99C4831-D037-6A95-9DCF-E1CEC537875D</vpstxid>
<txstateid>0</txstateid>
<amount>0</amount>
<started />
<completed />
<vpsauthcode>0</vpsauthcode>
<batchid>0</batchid>
<aborted />
<released />
<applyavscv2>0</applyavscv2>
<apply3dsecure>0</apply3dsecure>
<authattempt>0</authattempt>
<threedattempt>0</threedattempt>
<eci>0</eci>
<t3mscore>0</t3mscore>
<surcharge>0</surcharge>
</vspaccess>
To a class with this structure :
[XmlRoot("vspaccess")]
public class GetTransactionDetailResponse
{
[XmlElement(Type=typeof(TransactionDetail), Order = 1)]
public TransactionDetail Detail { get; set; }
}
public class TransactionDetail
{
[XmlElement("vpstxid", Order = 1)]
public string VpsTxId { get; set; }
// Rest of properties
}
However, when I try it, the "Detail" property of the class GetTransactionDetailResponse
always is null.
I've tried combining a lot of overloads of the XmlElement attribute but the result
is always null.
I can't modify the xml structure because is returned from a web service and the structure
of the class GetTransactionDetailResponse is fixed because is used in a lot of places in the code (is legacy code).
There are a lot of samples in stackoverflow in relation to serializing/deserializing XML from/to objects, but i haven't found any that is similar to my case.
You can modify the TransactionDetail class to be like this:
[XmlRoot("vspaccess")]
public class TransactionDetail
{
public string vpstxid;
public byte txstateid;
public byte amount;
public object started;
public object completed;
public byte vpsauthcode;
public byte batchid;
public object aborted;
public object released;
public byte applyavscv2;
public byte apply3dsecure;
public byte authattempt;
public byte threedattempt;
public byte eci;
public byte t3mscore;
public byte surcharge;
}
Then you can write de-serialization code like this:
XmlSerializer serializer = new XmlSerializer(typeof(TransactionDetail));
TransactionDetail td = null;
using (StreamReader reader = new StreamReader(path))
{
td = (TransactionDetail)serializer.Deserialize(reader);
}
Finally, you can set Detail property of the GetTransactionDetailResponse object with the TransactionDetail object that you just deserialized.
I'm trying to serialize a class that derives from BindingList(Floor), where Floor is a simple class that only contains a property Floor.Height
Here's a simplified version of my class
[Serializable]
[XmlRoot(ElementName = "CustomBindingList")]
public class CustomBindingList:BindingList<Floor>
{
[XmlAttribute("publicField")]
public string publicField;
private string privateField;
[XmlAttribute("PublicProperty")]
public string PublicProperty
{
get { return privateField; }
set { privateField = value; }
}
}
I'll serialize an instance of CustomBindingList using the following code.
XmlSerializer ser = new XmlSerializer(typeof(CustomBindingList));
StringWriter sw = new StringWriter();
CustomBindingList cLIst = new CustomBindingList();
Floor fl;
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
ser.Serialize(sw, cLIst);
string testString = sw.ToString();
Yet testString above ends getting set to the following XML:
<CustomBindingList xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
<Floor Height="10" />
<Floor Height="10" />
<Floor Height="10" />
</CustomBindingList>"
How do I get "publicField" or "publicProperty to serialize as well?
The short answer here is that .NET generally expects something to be a collection xor to have properties. This manifests in a couple of places:
xml serialization; properties of collections aren't serialized
data-binding; you can't data-bind to properties on collections, as it implicitly takes you to the first item instead
In the case of xml serialization, it makes sense if you consider that it might just be a SomeType[] at the client... where would the extra data go?
The common solution is to encapsulate a collection - i.e. rather than
public class MyType : List<MyItemType> // or BindingList<...>
{
public string Name {get;set;}
}
public class MyType
{
public string Name {get;set;}
public List<MyItemType> Items {get;set;} // or BindingList<...>
}
Normally I wouldn't have a set on a collection property, but XmlSerializer demands it...
XML serialization handles collections in a specific way, and never serializes the fields or properties of the collection, only the items.
You could either :
implement IXmlSerializable to generate and parse the XML yourself (but it's a lot of work)
wrap your BindingList in another class, in which you declare your custom fields (as suggested by speps)
This is known issue with XML serialization and inheriting from collections.
You can read more info on this here : http://social.msdn.microsoft.com/Forums/en-US/asmxandxml/thread/0d94c4f8-767a-4d0f-8c95-f4797cd0ab8e
You could try something like this :
[Serializable]
[XmlRoot]
public class CustomBindingList
{
[XmlAttribute]
public string publicField;
private string privateField;
[XmlAttribute]
public string PublicProperty
{
get { return privateField; }
set { privateField = value; }
}
[XmlElement]
public BindingList<Floor> Floors = new BindingList<Floor>();
}
This means you can add floors by using Floors.Add and you will get the result you want, I hope, however, I didn't try it. Keep in mind that playing around with attributes is the key to XML serialization.
Could you guys help me I have a problem with deserialization via IXmlSerializable
var ArrayOfAccounts = new Accounts(); //This class structure I'm trying to read
Class Accounts:List<Session>{ }
Class Shedule{
public DateTime StartAt { get; set; }
public DateTime EndAt { get; set; }
}
Class Session:IXmlSerializable {
public string Name{get;set;}
public string Pass{get;set;}
public List<Shedule> Shedules = new List<Shedule>();
public void ReadXml(System.Xml.XmlReader reader){
//AND HERE IS A PROBLEM. I don't know how to implement right code here. I've tried
//code below, but this one works for the first account only, and doesn't restore others
Schedules.Clear();
XmlReader subR = reader.ReadSubtree();
if (reader.MoveToAttribute("Name"))
Name = reader.Value;
if (reader.MoveToAttribute("Password"))
Password = reader.Value;
reader.MoveToContent();
while (subR.ReadToFollowing("Schedule"))
{
XmlSerializer x = new XmlSerializer(typeof(Schedule));
object o = x.Deserialize(subR);
if (o is Schedule) Schedules.Add((Schedule)o);
}
}
And the xml itself looks like:
<Accounts>
<Session UserName="18SRO" Password="shalom99">
<Schedule>
<StartAt>0001-01-01T09:30:00</StartAt>
<EndAt>0001-01-01T16:00:00</EndAt>
</Schedule>
</Session>
</Accounts>
Since you've defined the classes, you should just be able to use XML Serialization attributes, and use the default XML deserializer.
Your structure doesn't look overly complicated, is there any particular reason you're not using serialization attributes instead of manually deserializing?
Re inherited fields... if you switch to DataContractSerializer, then fields are "opt in" rather than "opt out" - but you lose the ability to specify attributes (everything is an element). A trivial example:
[DataContract(Name="foo")]
public class Foo
{
[DataMember(Name="bar")]
public string Bar { get; set; }
public int ThisIsntSerialized {get;set;}
}
However - adding unexpected subclasses is a pain for both XmlSerializer and DataContractSerializer. Both can do it, but it isn't pretty...