XML serialization - bypass container class? - c#

I am trying to serialize an object in a particular manner. The main class has a container class that holds some attributes, but really these attributes should be on the main class, from the point of view of the schema. Is there a way to bypass the container class and treat the properties on the container class as properties on the main class, for the purposes of serialization?
I am trying to create XML along the lines of:
<Main foo="3" bar="something">
<Others>etc</Others>
</Main>
from this code:
[System.Xml.Serialization.XmlRootAttribute("Main", Namespace = "")]
public class MainObject
{
public HelperContainer { get; set; }
public string Others { get; set; }
}
public class HelperContainer
{
[System.Xml.Serialization.XmlAttributeAttribute(AttributeName = "foo")]
public int Foo { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute(AttributeName = "bar")]
public string Bar { get; set; }
}

You may want to try implementing IXmlSerializable on MainObject to be able to control what happens when calling serialize. For the read and write xml methods specify the fields to serialize.
Check out msdn: http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
Something like:
public class MainObject : IXmlSerializable
{
public HelperContainer { get; set; }
public string Others { get; set; }
public void WriteXml (XmlWriter writer)
{
writer.WriteString(Others);
writer.WriteAttributeString("foo", HelperContainer.Foo);
writer.WriteAttributeString("bar", HelperContainer.Bar);
}
public void ReadXml (XmlReader reader)
{
Others = reader.ReadString();
//...
}
}

Related

Deserializing Generic that implements Interface with a List<T> of Interfaces .NET C#

Scenario
I have some XML come down from a service that I want to deserialize.
Depending on what is returned from the service, the XML can vary slightly (with the element names); but the XML always follows a common structure.
Here is a sample of what the XML might look like:
<ATemplate>
<Name>SomeTemplate</Name>
<TemplateItems>
<ATemplateItem>
<Name>SomeTemplateItem</Name>
<TemplateFields>
<ATemplateField>
<Name>SomeTemplateField</Name>
<Colour>Blue</Colour>
</ATemplateField>
... more template fields
</TemplateFields>
</ATemplateItem>
... more template items
</TemplateItems>
</ATemplate>
Using the above XML as an example, I have created a ATemplate class that will deserialize nicely from the XML, using the ATemplateItem and ATemplateField classes accordingly:
public class ATemplate
{
public string Name { get; set; }
public List<ATemplateItem> TemplateItems { get; set; }
}
public class ATemplateItem
{
public string Name { get; set; }
public List<ATemplateField> TemplateFields { get; set; }
}
public class ATemplateField
{
public string Name { get; set; }
public string Colour { get; set; }
}
I use this code to deserialize:
ATemplate template;
using (TextReader reader = new StringReader(xmlString))
{
template = (ATemplate)new XmlSerializer(typeof(ATemplate)).Deserialize(reader);
}
All good, so far.
Curveball
The same scenario might occur where the XML contains BTemplate, BTemplateItems and BTemplateFields; still following the structure as above.
So I created other classes for this situation:
public class BTemplate { ... }
public class BTemplateItem { ... }
public class BTemplateField { ... }
And made the relevant classes inherit respectively from ITemplate, ITemplateItem and ITemplateField I created, also:
Interfaces
public class ITemplate
{
public string Name { get; set; }
public List<ITemplateItem> TemplateItems { get; set; }
}
public class ITemplateItem
{
public string Name { get; set; }
public List<ITemplateField> TemplateFields { get; set; }
}
public class ITemplateField
{
public string Name { get; set; }
public string Colour { get; set; }
}
This is so I can then create one function, which is able to loop through the ITemplateItems and their ITemplateFields and perform some cool stuff:
public void Foo(ITemplate template)
{
foreach (var item in template.TemplateItems)
{
// do cool stuff
foreach (var field in item.TemplateFields)
{
// do more cool stuff
}
}
}
Some things to note:
In the object that contains the XML, I know what "type" the XML contains - given an Enum I use to identify
I then use a switch statement to run different methods, depending on the said "type"
Generic Method?
Now, rather than deserializing the XML differently in each of those method cases, I would like to use a Generic method to deserialize.
So I created one, like this:
public ITemplate DeserializeTemplate<T>(string xmlString) where T : ITemplate
{
using (TextReader reader = new StringReader(xmlString))
{
return (T)new XmlSerializer(typeof(T)).Deserialize(reader);
}
}
And call it from within the specific methods like so:
var template = DeserializeTemplate<ATemplate>(xmlString);
Then, I can use the ITemplate that it returns, and pass it to Foo(ITemplate template) to go and perform some magic and wizardry.
But...
No compilation errors, as yet - however I get a RunTime error, because it cannot deserialize an Interface.
I gather this is because it's trying to then deserialize the ITemplate's TemplateItems as ITemplateItems.
Can I do the above?
My question is:
How can I get around this issue?
Can I use this Generic deserialize method?
Will I need to treat each one differently in the separate methods?
Will I need to make the Interface generic also, with the types to expect?
I'm banging my head against the desk, so I really hope you lovely SO people can help.
As always, your comments, answers and suggestions are much appreciated :)

Nest XML Elements - XML Serialization

I need to sandwich an element inside of another element. Is it possible to serialize XML like this?
http://pastebin.com/7qDE7Ses
Here is my class
[XmlRoot(ElementName = "SalesOrderMod")]
public partial class SalesOrderMod
{
[XmlElementAttribute(Order = 1)]
public string TxnID { get; set; }
[XmlElementAttribute(Order = 2)]
public string EditSequence { get; set; }
[XmlElementAttribute(Order = 3)]
public string ShipDate { get; set; }
[XmlElementAttribute(Order = 4)]
public ListRef ShipMethodRef = new ListRef();
public bool ShouldSerializeShipMethodRef()
{
return !(String.IsNullOrEmpty(ShipMethodRef.FullName));
}
[XmlElementAttribute(Order = 5)]
public string Other { get; set; }
[XmlElementAttribute(Order = 6, ElementName = "SalesOrderLineMod")]
public List<LineMod> SalesOrderLineMod = new List<LineMod>();
[XmlElementAttribute(Order = 7, ElementName = "SalesOrderLineGroupMod")]
public List<LineMod> SalesOrderLineGroupMod = new List<LineMod>();
}
You originally indicated you would like to serialize a series of elements inside an XML document like so:
<SalesOrderLineRet>
</SalesOrderLineRet>
<SalesOrderLineGroupRet>
</SalesOrderLineGroupRet>
<SalesOrderLineRet>
</SalesOrderLineRet>
You can if the types that correspond to SalesOrderLineRet and SalesOrderLineGroupRet have some common base type T, and they are stored in a List<T>. For instance, the following class definitions:
public abstract class SalesOrderLineRetBase
{
}
public class SalesOrderLineRet : SalesOrderLineRetBase
{
}
public class SalesOrderLineGroupRet : SalesOrderLineRetBase
{
}
public class RootObject
{
[XmlElement(typeof(SalesOrderLineRetBase))]
[XmlElement(typeof(SalesOrderLineRet))]
[XmlElement(typeof(SalesOrderLineGroupRet))]
public List<SalesOrderLineRetBase> SalesOrders { get; set; }
}
Will, when serialized, produce the following XML:
<RootObject>
<SalesOrderLineRet />
<SalesOrderLineGroupRet />
</RootObject>
Using [XmlElement(typeof(T))] tells XmlSerializer that the list should be serialized without an outer container element, and that items of type T can be expected to be found in the list. You must apply [XmlElement(typeof(T))] once for each type T that will be stored in the list.
(You can use List<object> if the types in the list have no other more derived base type, however I don't recommend that. I would instead recommending grouping the possible types of list entry under a specific base type.)
If you would prefer your list to be serialized with an outer container element, you can use [XmlArray] and [XmlArrayItem(typeof(T))]:
public abstract class SalesOrderLineRetBase
{
}
public class SalesOrderLineRet : SalesOrderLineRetBase
{
}
public class SalesOrderLineGroupRet : SalesOrderLineRetBase
{
}
public class RootObject
{
[XmlArray("SalesOrders")]
[XmlArrayItem(typeof(SalesOrderLineRetBase))]
[XmlArrayItem(typeof(SalesOrderLineRet))]
[XmlArrayItem(typeof(SalesOrderLineGroupRet))]
public List<SalesOrderLineRetBase> SalesOrders { get; set; }
}
Which produces the following XML:
<RootObject>
<SalesOrders>
<SalesOrderLineRet />
<SalesOrderLineGroupRet />
</SalesOrders>
</RootObject>
You must apply [XmlArrayItem(typeof(T))] for each type T that will be stored in the list.
(Since you don't include the relevant classes and XML in your question, I'm not sure which one you might want.)

Populate custom List sub class from XML document via LINQ

I have figured out how to populate a custom class from XML data, but I ran into an issue along the way. Things were working perfectly with my existing method of populating data until I was thrown a bit of a curve ball. The new schema I was sent is similar to this:
<ITEM_REPLY>
<TRAN_ID>1320691307345</TRAN_ID>
<REPLY_CODE>0</REPLY_CODE>
<UNIT_PRICE>8.2784</UNIT_PRICE>
<SUP_LOCS>
<SUP_LOC>
<SUP_LOC_ID>001134</SUP_LOC_ID>
<COUNTRY_ID>USA</COUNTRY_ID>
<QTY_AVL>47.000</QTY_AVL>
<ITEM_UOM>EA</ITEM_UOM>
</SUP_LOC>
<SUP_LOC>
<SUP_LOC_ID>006817</SUP_LOC_ID>
<COUNTRY_ID>USA</COUNTRY_ID>
<QTY_AVL>20.000</QTY_AVL>
<ITEM_UOM>EA</ITEM_UOM>
</SUP_LOC>
</SUP_LOCS>
<MESSAGE />
<QTY_BREAKS />
</ITEM_REPLY>
Pretty standard XML schema, problem is I'm not sure how to populate my custom class with it. Here's what I do have:
static void Main(string[] args)
{
var order = ConvertXMLMessage<ItemReply>(request);
}
protected static T ConvertXMLMessage<T>(String xmlData) where T : class, new()
{
var xml = new XmlDocument();
xml.LoadXml(xmlData);
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (var xmlReader = new XmlNodeReader(xml.DocumentElement))
{
T work = (T)(serializer.Deserialize(xmlReader));
return work;
}
}
public class ItemReply
{
[XmlElement("ITEM_REPLY")]
public ItemAvlReply ITEM_REPLY { get; set; }
}
public class ItemAvlReply
{
[XmlElement("TRAN_ID")]
public string TRAN_ID { get; set; }
[XmlElement("REPLY_CODE")]
public string REPLY_CODE { get; set; }
[XmlElement("UNIT_PRICE")]
public string UNIT_PRICE { get; set; }
[XmlElement("SUP_LOCS")]
public SupplierLocations SUP_LOCS;
[XmlElement("MESSAGE")]
public string MESSAGE { get; set; }
[XmlElement("QTY_BREAKS")]
public string QTY_BREAKS { get; set; }
}
public class SupplierLocations
{
[XmlElement("SUP_LOC")]
public List<SupplierLocation> SUP_LOC;
}
public class SupplierLocation
{
[XmlElement("SUP_LOC_ID")]
public string SUP_LOC_ID { get; set; }
[XmlElement("COUNTRY_ID")]
public string COUNTRY_ID { get; set; }
[XmlElement("QTY_AVL")]
public string QTY_AVL { get; set; }
[XmlElement("ITEM_UOM")]
public string ITEM_UOM { get; set; }
}
This works perfectly minus the List<Item> part. I'm not overly experienced with LINQ and I'm not sure how to go about declaring a sub array in my class via this statement. I am also open to a different approach from creating the List<Item> part, I'm just not sure where to start otherwise. Is there a better approach for what I'm need to do? Is there an easy solution I am just unaware of in LINQ?
Here's a simple way to do it, assuming the example XML file you provided has typos. I assumed the OrderId has a closing tag, and that the closing tag for Items should be /Items.
Here's the version of the xml I used:
<Order>
<TransactionID>123</TransactionID>
<OrderID>1</OrderID>
<Items Number="2">
<Item>
<ItemName>Test</ItemName>
<Color>Red</Color>
</Item>
<Item>
<ItemName>Test1</ItemName>
<Color>Blue</Color>
</Item>
</Items>
</Order>
Here's the code to read/write the XML: (the xml variable is a String)
var order = ConvertXMLMessage<Order>(xml);
WriteXMLFile<Order>(order, #"test.xml");
Here's the ConvertXMLMessage and WriteXMLFile functions:
protected static T ConvertXMLMessage<T>(String xmlData) where T : class, new()
{
var xml = new XmlDocument();
xml.LoadXml(xmlData);
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (var xmlReader = new XmlNodeReader(xml.DocumentElement))
{
T work = (T)(serializer.Deserialize(xmlReader));
return work;
}
}
protected static void WriteXMLFile<T>(T item, String saveLocation) where T : class, new()
{
System.Xml.Serialization.XmlSerializer writer = new System.Xml.Serialization.XmlSerializer(typeof(T));
System.IO.StreamWriter file = new System.IO.StreamWriter(saveLocation);
writer.Serialize(file, item);
file.Close();
}
and here's the class structure:
public class Order
{
[XmlElement("TransactionID")]
public string TransactionId { get; set; }
[XmlElement("OrderID")]
public string OrderId { get; set; }
[XmlElement("Items")]
public ItemsContainer Items;
}
public class ItemsContainer
{
[XmlAttribute("Number")]
public Int32 Number { get; set; }
[XmlElement("Item")]
public List<Item> Items;
}
public class Item
{
[XmlElement("ItemName")]
public string ItemName { get; set; }
[XmlElement("Color")]
public string Color { get; set; }
}
As you'll notice I added some attributes to let the XML parser know how to handle the class when it's converting from/to the XML. I also added another small class called "ItemsContainer" just to hold the details on the Items tag. If you didn't need the "Number" attribute, then you could probably find a way to do away with this. However, this should get you in the right direction.
The example I provided is a simple version of how I usually handle the situation, obviously there's some improvements you can make depending on your needs.
Edit
I changed the Item class to use ItemName instead of TransactionId. It was an oversight on my part.
Edit 2
Here's the corrections you need to make to the newly posted code. The reason the Order class worked in the previous example was it matched the root XML element. You're new XML does align with the base class. So we need to add in a couple more attributes to make this work. You can also remove your ItemReply class. It's not needed.
So here's the new classes:
[XmlRoot("ITEM_REPLY")]
public class ItemAvlReply
{
[XmlElement("TRAN_ID")]
public string TRAN_ID { get; set; }
[XmlElement("REPLY_CODE")]
public string REPLY_CODE { get; set; }
[XmlElement("UNIT_PRICE")]
public string UNIT_PRICE { get; set; }
[XmlElement("SUP_LOCS")]
public SupplierLocations SUP_LOCS;
[XmlElement("MESSAGE")]
public string MESSAGE { get; set; }
[XmlElement("QTY_BREAKS")]
public string QTY_BREAKS { get; set; }
}
public class SupplierLocations
{
[XmlElement("SUP_LOC")]
public List<SupplierLocation> SUP_LOC;
}
public class SupplierLocation
{
[XmlElement("SUP_LOC_ID")]
public string SUP_LOC_ID { get; set; }
[XmlElement("COUNTRY_ID")]
public string COUNTRY_ID { get; set; }
[XmlElement("QTY_AVL")]
public string QTY_AVL { get; set; }
[XmlElement("ITEM_UOM")]
public string ITEM_UOM { get; set; }
}
Everything else should remain the same. The parsing/converting the XML to classes should work without any changes.

Which must be used with XmlSerializer and WCF - DataContract or MessageContract?

I need to write WCF service wich will take .xml file deserialize it into my custom calss and then do some operations on that data. I have written test version of this application which use console but not WCF. Here is how my code looks like:
[Serializable]
public class ErrorMsgElement
{
[XmlElement("Sender")]
public string SenderOfMessage{ get; set; }
[XmlElement()]
public Int64 UserID { get; set; }
[XmlElement()]
public Int64 SerialNumber { get; set; }
[XmlElement("DateTime")]
public DateTime DateAndTimeOfMessage { get; set; }
}
[Serializable]
[XmlRoot("Root")]
public class ErrorMessage
{
[XmlElement("Header")]
public string HeaderOfFile { get; set; }
[XmlElement("ErrorMsg")]
public ErrorMsgElement msgElent { get; set; }
}
And Main code
ErrorMessage myErrorMsg = new ErrorMessage();
//pathToFile is original path
string path = #pathToFile;
XmlSerializer myXmlSerializer = new XmlSerializer(typeof(ErrorMessage));
using (StreamReader myStrReader = new StreamReader(path))
{
myErrorMsg = (ErrorMessage)myXmlSerializer.Deserialize(myStrReader);
}
//some operations on myErrorMsg such as writing to database
It works fine in console application. Now I need to write same logic in WCF service. But I don't know what should I use - [Serializable], [DataContact], [MessageContract] or someting else?. Consider that I have some [XmlArray] atributes in my custom classes as well. And if I add [DataContract] atribute to my ErrorMessage class do I have to add same attribute to ErrorMsgElement as following?
[DataContract]
[Serializable]
public class ErrorMsgElement
{
...
}
[DataContract]
[Serializable]
[XmlRoot("Root")]
public class ErrorMessage
{
...
[DataMember]
[XmlElement("ErrorMsg")]
public ErrorMsgElement msgElent { get; set; }
}
All answers are well appreciated. Thanks in advance.
in wcf [DataContact] and DataContractSerializer are recommended for what do you want to do. But you can also use XmlSerializer as well it's really only your choice(using first or second don't making diffrent for performance of your application).
Edit:
When you adding DataContract attribute to your class than you don't have to add Serializable attribute too the same class DataContract is equal to Serializable
check this for answer about nested DataContracts

C#, XML to Class DeSerialization Question

I have the following XML snippet:
<dmFiles>
−
<dmFile dmUpFileGuid="" dmFileDescr="testcase01.pdf" dmFileMetaType="main" dmFileGuid="" dmMimeType="pdf" dmFormat="">
If I create a strongly typed C# class with string properties for the dmFile attributes (eg: dmFileDescr), how can I ensure these attributes will serialize to properties in my c# class?
By using Xml attributes on your class member. Use [XmlAttribute("name")].
Your implementation would look like this:
[XmlRoot("dmFile")]
public class DmFile
{
[XmlAttribute("dmUpFileGuid")]
public String UpFileGuid { get;set; }
...
}
Try this:
[Serializable]
[XmlRoot(ElementName="dmFile")]
public class File
{
[XmlAttribute(AttributeName="dmUpFileGuid")]
public string UploadGuid { get; set; }
[XmlAttribute(AttributeName = "dmFileDescr")]
public string Description { get; set; }
[XmlAttribute(AttributeName = "dmFileMetaType")]
public string MetaType { get; set; }
[XmlAttribute(AttributeName = "dmFileGuid")]
public string FileGuid { get; set; }
[XmlAttribute(AttributeName = "dmMimeType")]
public string MimeType { get; set; }
[XmlAttribute(AttributeName = "dmFormat")]
public string Format { get; set; }
}
And deserialize your XML as follow:
XmlSerializer s = new XmlSerializer(typeof(File));
File file = s.Deserialize(new StringReader(#"<dmFile ... />")) as File;
You can (de)serialize from/to XML with the XmlSerializer and marking up the target class with Attributes provided for Xml-Serialization.
Mark your public properties with the correct attribute. It should be XmlAttributeAttribute.
The enclosing class must map on the dmFile-Element (XmlRootAttribute)
If the property is called differently, or the class is called differently than the XML element, you need to specify the XML-Element/Attribute name.

Categories

Resources