Control the order of attributes when serialising - c#

Let me start off by saying I've seen the following questions:
Controlling order of serialization in C#: It is about element order
How to specify the order of XmlAttributes, using XmlSerializer: The answer just says "You don't"
net xmlserializer : keeping attributes order: Again says "you don't" or the alternate answer states that you can but you have to do all of the serialisation yourself
I've also seen people state that the order that you specify the attributes in the class is the order that they will be serialised. However, in my case I have a base class that has attributes too so this doesn't seem to work out.
I have the following two classes:
public class MyClass : BaseClass
{
[XmlAttribute()]
public string Value1 { get; set; }
public MyClass()
: base()
{
Value1 = string.Empty;
}
}
public class BaseClass
{
[XmlAttribute()]
public string Value2 { get; set; }
[XmlAttribute()]
public string Value3 { get; set; }
public BaseClass()
{
Value2 = string.Empty;
Value3 = string.Empty;
}
}
When serialised to an XML element's attributes they appear in the order, Value2, Value3, Value1. I'd like them to appear in the order Value1, Value2, Value3.
I use the following method to save the XML to disk:
public static void Save<T>(string path, T contents)
{
using (StreamWriter sw = new StreamWriter(File.OpenWrite(path)))
{
XmlSerializer xml = new XmlSerializer(typeof(T));
xml.Serialize(sw, contents, ns);
}
}
Is there any way I can specify the attribute order? Like with elements:
[XmlElementAttribute(Order = 1)]
To those who will say something like "Order doesn't matter" or "If it works why do you need to change the order" or "XML isn't meant to be readable". My answer is yes the order doesn't matter and it works. However, when I'm debugging my code and checking the XML to make sure it contains the values I expect, it is a lot easier to read if the attributes are in the order I want them to be in.

Related

How to deserialize a tag nested within a text section of another tag?

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.

Xml serialization: serialize list of custom classes, derived from the same base class, using base class name for elements

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.

Write XML without CData

I want to write XML, and the output is below part in XML like ..<abc><![CDATA[stackoverflow]]></abc>..
[XmlIgnore]
public string abc { get; set; }
[XmlElement("abc")]
public System.Xml.XmlCDataSection abc_NoCDATA
{
get
{
return new System.Xml.XmlDocument().CreateCDataSection(abc);
}
set
{
abc = value.Value;
}
}
How can I write XML without CDATA?
You're explicitly returning an XmlCDataSection, this doesn't make sense if you don't want one.
Simply make abc the actual XmlElement that you output. This should be suffice:
[XmlElement("abc")]
public string abc { get; set; }
If you want more control, consider using XmlDocument or XDocument classes directly to create your XML Document from the start, rather than serialization.

Public fields/properties of a class derived from BindingList<T> wont serialize

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.

IXmlSerializable

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...

Categories

Resources