Serialize Property as Xml Attribute in Element - c#

I have the following class:
[Serializable]
public class SomeModel
{
[XmlElement("SomeStringElementName")]
public string SomeString { get; set; }
[XmlElement("SomeInfoElementName")]
public int SomeInfo { get; set; }
}
Which (when populated with some test data) and Serialized using XmlSerializer.Serialize() results in the following XML:
<SomeModel>
<SomeStringElementName>testData</SomeStringElementName>
<SomeInfoElementName>5</SomeInfoElementName>
</SomeModel>
What I need to have is:
<SomeModel>
<SomeStringElementName Value="testData" />
<SomeInfoElementName Value="5" />
</SomeModel>
Is there a way to specify this as attributes without writing my own custom serialization code?

You will need wrapper classes:
public class SomeIntInfo
{
[XmlAttribute]
public int Value { get; set; }
}
public class SomeStringInfo
{
[XmlAttribute]
public string Value { get; set; }
}
public class SomeModel
{
[XmlElement("SomeStringElementName")]
public SomeStringInfo SomeString { get; set; }
[XmlElement("SomeInfoElementName")]
public SomeIntInfo SomeInfo { get; set; }
}
or a more generic approach if you prefer:
public class SomeInfo<T>
{
[XmlAttribute]
public T Value { get; set; }
}
public class SomeModel
{
[XmlElement("SomeStringElementName")]
public SomeInfo<string> SomeString { get; set; }
[XmlElement("SomeInfoElementName")]
public SomeInfo<int> SomeInfo { get; set; }
}
And then:
class Program
{
static void Main()
{
var model = new SomeModel
{
SomeString = new SomeInfo<string> { Value = "testData" },
SomeInfo = new SomeInfo<int> { Value = 5 }
};
var serializer = new XmlSerializer(model.GetType());
serializer.Serialize(Console.Out, model);
}
}
will produce:
<?xml version="1.0" encoding="ibm850"?>
<SomeModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SomeStringElementName Value="testData" />
<SomeInfoElementName Value="5" />
</SomeModel>

Kind of, use the XmlAttribute instead of XmlElement, but it won't look like what you want. It will look like the following:
<SomeModel SomeStringElementName="testData">
</SomeModel>
The only way I can think of to achieve what you want (natively) would be to have properties pointing to objects named SomeStringElementName and SomeInfoElementName where the class contained a single getter named "value". You could take this one step further and use DataContractSerializer so that the wrapper classes can be private. XmlSerializer won't read private properties.
// TODO: make the class generic so that an int or string can be used.
[Serializable]
public class SerializationClass
{
public SerializationClass(string value)
{
this.Value = value;
}
[XmlAttribute("value")]
public string Value { get; }
}
[Serializable]
public class SomeModel
{
[XmlIgnore]
public string SomeString { get; set; }
[XmlIgnore]
public int SomeInfo { get; set; }
[XmlElement]
public SerializationClass SomeStringElementName
{
get { return new SerializationClass(this.SomeString); }
}
}

Related

Xml Serialization for list Property doesn't work when one item in the list

<TotalRecords ItineraryCount='1' >
<Recs ItineraryNumber="1" >
<Amount>516.6</Amount>
<TravelTime>940</TravelTime>
<FSegment>
<OutProperty>
<Segment No="1">
<Name>Ronald</Name>
<City>London</City>
<Country>United Kingdom</Country>
</Segment>
<Segment No="2">
<Name>Richard</Name>
<City>
London
</City>
<Country>United Kingdom</Country>
</Segment>
</OutProperty>
</FSegment>
</Recs>
</TotalRecords >
I am serializing xml to object of TotalRecords Class. It works fine when there are more than one segment in the OutProperty but in case of one segment it doesn't serialize into list property.
I have also tried with [XmlArray("")] and [XMlArrayItem("")] but it doesn't work. Anyone have idea?
public class TotalRecords
{
public Recs recs { get; set; }
public string ItineraryCount { get; set; }
}
public partial class Recs
{
public string amountField { get; set; }
public string travelTimeField { get; set; }
public FSegment fSegmentField { get; set; }
public string itineraryNumberField { get; set; }
}
public class FSegment
{
public List<Segment> OutProperty {get;set;}
}
public class Segment
{
public string nameField { get; set; }
public string cityField { get; set; }
public string countryField { get; set; }
}
Try to use the following for your Classes and their Properties:
[DataContract]
public class Contact
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
}
Hacking like this helps in many cases, perhaps in your too (converting List to array):
public class FSegment
{
[XmlIgnore]
public List<Segment> OutProperty {get;set;}
[XmlArray("OutProperty");
public Segment[] _OutProperty
{
get { return OutProperty.ToArray(); }
set { OutProperty = new List<Segment>(value); }
}
}
I am not exactly sure how your provided class definition worked previously without any XML attribute/element definitions since your variable names are not the same as the XML identifiers, meaning that the serialization wouldn't populate the properties that it couldn't find a name for.
Nether the less, the main issue here is that you are trying to put a list into a normal property.
From the XML provided, OutProperty is a single sub element of FSegment and Segment is an array of sub elements of OutProperty, but in your provided code, you tried to make Segment an array sub element of FSegment.
The correct structure of the class definition should be as follows (also including the XML definitions)
[System.Xml.Serialization.XmlRoot(ElementName="TotalRecords")]
public class TotalRecords
{
[System.Xml.Serialization.XmlElement(ElementName="Recs")]
public Recs recs { get; set; }
[System.Xml.Serialization.XmlAttribute(AttributeName = "ItineraryCount")]
public string ItineraryCount { get; set; }
}
public partial class Recs
{
[System.Xml.Serialization.XmlElement(ElementName = "Amount")]
public string amountField { get; set; }
[System.Xml.Serialization.XmlElement(ElementName = "TravelTime")]
public string travelTimeField { get; set; }
[System.Xml.Serialization.XmlElement(ElementName = "FSegment")]
public FSegment fSegmentField { get; set; }
[System.Xml.Serialization.XmlAttribute(AttributeName = "ItineraryNumber")]
public string itineraryNumberField { get; set; }
}
public class FSegment
{
[System.Xml.Serialization.XmlElement(ElementName = "OutProperty")]
public SegmentList OutProperty { get; set; }
}
public class SegmentList
{
[System.Xml.Serialization.XmlElement(ElementName = "Segment")]
public List<Segment> segmentField { get; set; }
}
public class Segment
{
[System.Xml.Serialization.XmlElement(ElementName = "Name")]
public string nameField { get; set; }
[System.Xml.Serialization.XmlElement(ElementName = "City")]
public string cityField { get; set; }
[System.Xml.Serialization.XmlElement(ElementName = "Country")]
public string countryField { get; set; }
[System.Xml.Serialization.XmlAttribute(AttributeName = "No")]
public int segmentNoField { get; set; }
}
Please note that in the above structure, Segment is a list object under the OutProperty object, which resides under the FSegment object.
Using this class structure to load your XML produces the (assumed) correct data in the created objects.
Using the XML definitions allows you to decouple the actual names of class properties from what they are called in the XML data. This allows you to changes the one or the other without affecting the other one, which is useful when you don't control the creation of the XML data.
If you don't want all the XML definitions in your code, change the names of the classes so that you can name your properties the same as what they are called in the XML file. That will allow you couple everything directly

XML Deserialization results in empty object

I'm attempting to deserialize this XML file into an object
<?xml version="1.0" encoding="utf-8" ?>
<rules version="3">
<emie>
<domain>msdn.microsoft.com</domain>
<domain exclude="false">
bing.com
<path exclude="true">images</path>
</domain>
<domain exclude="true">
news.msn.com
<path exclude="false">pop-culture</path>
</domain>
<domain>timecard</domain>
<domain>tar</domain>
</emie>
</rules>
I have my objects laid out like so
[XmlRoot("rules")]
public class Rules {
[XmlAttribute("version")]
public string Version { get; set; }
[XmlElement("emie")]
public EMIE EMIE { get; set; }
}
public class EMIE {
[XmlArrayItem("Domain")]
public List<Domain> Domains { get; set; }
}
public class Domain {
[XmlAttribute("exclude")]
public bool Exclude { get; set; }
[XmlText]
public string Value { get; set; }
[XmlArrayItem("Path")]
public List<Path> Paths { get; set; }
}
public class Path {
[XmlAttribute]
public bool Exclude { get; set; }
[XmlText]
public string Value { get; set; }
}
And am using this code to deserialize it
static void Main(string[] args) {
XmlSerializer serializer = new XmlSerializer(typeof(Rules));
using (FileStream stream = new FileStream("EM.xml", FileMode.Open)) {
Rules xml = (Rules)serializer.Deserialize(stream);
foreach (Domain d in xml.EMIE.Domains) {
Console.WriteLine(d.Value);
foreach (EnterpriseModeModel.Path p in d.Paths) {
Console.WriteLine(p.Value);
}
}
}
Console.ReadLine();
}
However, my rules.EMIE.Domains object is always empty. When I debug I can see my stream object has a length so it's properly picking up the data in the file but it never fills up the object like I expect it to.
Change the declaration of EMIE as follows
public class EMIE
{
[XmlElement("domain")]
public List<Domain> Domains { get; set; }
}

How to serialize object's property to XML element's attribute?

I've created a object that is serializable and I want to serialize it to XML and then later deserialize back. What I want though is to save one property of this object as XML attribute. Here is what I mean:
[Serializable]
public class ProgramInfo
{
public string Name { get; set; }
public Version Version { get; set; }
}
public class Version
{
public int Major { get; set; }
public int Minor { get; set; }
public int Build { get; set; }
}
I want to save ProgramInfo to XML file that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<ProgramInfo Name="MyApp" Version="1.00.0000">
</ProgramInfo>
Notice the Version property and its corresponding attribute in XML. I already have parser that turns string "1.00.0000" to valid Version object and vice-versa, but I don't know how to put it to use with this XML serialization scenario.
What you need is a property for the string representation that gets serialized:
[Serializable]
public class ProgramInfo
{
[XmlAttribute]
public string Name { get; set; }
[XmlIgnore]
public Version Version { get; set; }
[XmlAttribute("Version")
public string VersionString
{
get { return this.Version.ToString(); }
set{ this.Version = Parse(value);}
}
}
What you could do is have a VersionValue and a VersionType Property
[Serializable]
public class ProgramInfo
{
private string _versionValue;
public string Name { get; set; }
public string VersionValue
{
get
{
return _versionValue;
}
set{
_versionValue = value;
//Private method to parse
VersonType = parseAndReturnVersionType(value);
}
}
public Version VersionType { get; set; }
}

add an attribute in an element that is parent of a list

I am trying to serialize an object but I am facing some issues regarding the attributes of a parent element that contains an array.
I have the following xml structure and I can't add the attribute in RatePlans element.
<Root>
<RatePlans Attribute="??this one??">
<RatePlan Attribute1="RPC" Attribute2="MC" Attribute3="RPT">
.
.
.
</RatePlan>
<RatePlan Attribute1="RPC2" Attribute2="MC3" Attribute3="RPT4">
.
.
.
</RatePlan>
</RatePlans>
</Root>
This is what I have done so far:
namespace XmlT {
[Serializable]
[XmlRoot("Root")]
public class Root {
public List<RatePlan> RatePlans { get; set; }
}
}
namespace XmlT {
[Serializable]
public class RatePlan {
[XmlAttribute]
public string RatePlanCode { get; set; }
[XmlAttribute]
public string MarketCode { get; set; }
[XmlAttribute]
public string RatePlanType { get; set; }
}
}
This gives me a correct structure but I don't know how to add the attribute I want
Another approach
I've tried also another approach but this gives me wrong values at all.
namespace XmlT {
[Serializable]
[XmlRoot("Root")]
public class Root {
public RatePlans RatePlans { get; set; }
}
}
namespace XmlT {
[Serializable]
public class RatePlans {
[XmlAttribute]
public string HotelCode { get; set; }
public List<RatePlan> RatePlan { get; set; }
}
}
EDIT
this the method that I am using for the serialization
protected static string Serialize<T>(object objToXml, bool IncludeNameSpace = false) where T : class {
StreamWriter stWriter = null;
XmlSerializer xmlSerializer;
string buffer;
try {
xmlSerializer = new XmlSerializer(typeof(T));
MemoryStream memStream = new MemoryStream();
stWriter = new StreamWriter(memStream);
if (!IncludeNameSpace) {
var xs = new XmlSerializerNamespaces();
xs.Add("", "");
xmlSerializer.Serialize(stWriter, objToXml, xs);
} else {
xmlSerializer.Serialize(stWriter, objToXml);
}
buffer = Encoding.ASCII.GetString(memStream.GetBuffer());
} catch (Exception Ex) {
throw Ex;
} finally {
if (stWriter != null) stWriter.Close();
}
return buffer;
}
Does anyone know how could I do this?
Thanks
If the RatePlans class from the 2nd example inherits from List<RatePlan> you will get the desired result:
[Serializable]
public class RatePlans: List<RatePlan>
{
[XmlAttribute]
public string HotelCode { get; set; }
}
Edit:
My bad. Fields of classes inheriting from collections will not be serialized. I didn't knew that. Sorry...
However, this solution works:
[Serializable]
[XmlRoot("Root")]
public class Root
{
public RatePlans RatePlans { get; set; }
}
[Serializable]
public class RatePlans
{
[XmlAttribute]
public string HotelCode { get; set; }
[XmlElement("RatePlan")]
public List<RatePlan> Items = new List<RatePlan>();
}

Xml Deserialization - Sequence of Mutiple Types

Given the following fragment where links is a sequence of unbounded imagelinks and documentlinks, what should the deserailized class be?
<Values>
<Links>
<ImageLink>http://#</ImageLink>
<ImageLink>http://#</ImageLink>
<DocumentLink>http://#</DocumentLink>
</Links>
</Values>
Typically, if it was just an array of imagelinks I might have
public class Values
{
public imagelink[] ImageLinks { get; set; }
}
public class ImageLink
{
public string Value { get; set; }
}
But with the above xml I'm stumped.
Btw, I have no control over the xml.
This worked
public class DocumentLink : Link
{
}
public class ImageLink : Link
{
}
public class Link
{
[XmlText]
public string Href { get; set; }
}
public class Values
{
[XmlArrayItem(ElementName = "ImageLink", Type = typeof(ImageLink))]
[XmlArrayItem(ElementName = "DocumentLink", Type = typeof(DocumentLink))]
public Link[] Links { get; set; }
}
You should have a base class Link as follows
public class Link
{
public string Href { get; set; }
}
public class ImageLink : Link
{
}
public class DocumentLink : Link
{
}
And your values class would look like:
public class Values
{
public Link[] links { get; set; }
}
Alternatively, you could use ArrayList instead of strong typed array.

Categories