How to make following class objects to xml? - c#

I have following class. (SIMModel, Product, Item)
public class SIMModel
{
public Product Product { get; set; }
}
public class Product
{
public List<Item> Items { get; set; }
}
public class Item
{
[XmlAttribute("ID")]
public String ID { get; set; }
[XmlAttribute("Name")]
public String Name { get; set; }
public Child_Item Child_Item { get; set; }
public Parent_Item Parent_Item { get; set; }
}
public class Child_Item
{
[XmlAttribute("ID")]
public String ID { get; set; }
}
And I want to make this XML
<SIMModel>
<Product>
<Item ID="N" Name="N-1">
<Child_Item ID="N-1-1">
</Item>
</Proudct>
</SIMModel>
How can i make the Simulation XML using upper class?
I don't know how to wrap each class..

I made a simple Serialize method that serializes to string and removes the namespaces.
private static void Main(string[] args)
{
string result = Serialize(new SIMModel
{
Product = new Product
{
Items = new List<Item>
{
new Item
{
ID = "N",
Name = "N-1",
Child_Item = new Child_Item {ID = "N-1-1"}
}
}
}
});
Console.WriteLine(result);
}
public static string Serialize<T>(T value)
{
if (value == null)
{
return null;
}
//Create our own namespaces for the output
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
//Add an empty namespace and empty value
ns.Add("", "");
XmlSerializer serializer = new XmlSerializer(typeof (T));
XmlWriterSettings settings = new XmlWriterSettings
{
Encoding = new UnicodeEncoding(false, false),
Indent = true,
OmitXmlDeclaration = true
};
using (StringWriter textWriter = new StringWriter())
{
using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings))
{
serializer.Serialize(xmlWriter, value, ns);
}
return textWriter.ToString();
}
}
Output:
<SIMModel>
<Product>
<Items>
<Item ID="N" Name="N-1">
<Child_Item ID="N-1-1" />
</Item>
</Items>
</Product>
</SIMModel>

Related

How to add Xml attribute to a property at runtime

I need to serialize a class to xml. If a certain condition is met at run-time, I want to add an XML attribute to an element and assign it a value. Sometimes, the "Error" attribute will appear and sometimes it won't.
My code that serializes my objects:
public class XmlToolsRepo : IXmlTools
{
public string SerializeToXML<T>(object obj)
{
string results = null;
Encoding enc = Encoding.UTF8;
using (MemoryStream ms = new MemoryStream())
{
using (XmlTextWriter xw = new XmlTextWriter(ms, enc))
{
xw.Formatting = Formatting.None;
XmlSerializerNamespaces emptyNS = new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") });
XmlSerializer xSerializer = new XmlSerializer(typeof(T));
xSerializer.Serialize(xw, obj, emptyNS);
}
results = enc.GetString(ms.ToArray());
}
return results;
}
}
A class with a property that could have a new attribute at run-time:
[DataContract]
public class H204
{
[DataMember]
[XmlAttribute]
public string Code { get; set; }
[DataMember]
public string DW { get; set; }
}
When a condition is met I need for the XML to look like this:
<?xml version="1.0" encoding="UTF-8"?>
<H204 Code="A">
<DW Error="test" />
</H204>
Try following :
public class H204
{
[XmlAttribute(AttributeName = "Code")]
public string Code { get; set; }
[XmlElement(ElementName = "DW")]
public DW dw{ get; set; }
}
public class DW
{
[XmlAttribute(AttributeName = "Error")]
public string text { get; set; }
}

custom Xml Serializer to make value as an xml element

Classes:
public class Employee
{
public int Id { get; set; }
public PhoneNumber[] Numbers { get; set; }
}
public class PhoneNumber
{
public string Type { get; set; }
public string Number { get; set; }
}
Code to serialize:
var xmlSerializer = new XmlSerializer(typeof(Employee));
var xwSettings = new XmlWriterSettings {Indent = true, OmitXmlDeclaration = true};
string serializedResult;
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, xwSettings))
{
xmlSerializer.Serialize(writer, emp);
serializedResult = stream.ToString();
}
Current Result:
<Employee>
<Id>1</Id>
<Numbers>
<PhoneNumber>
<Type>Home</Type>
<Number>1231231231</Number>
</PhoneNumber>
<PhoneNumber>
<Type>Office</Type>
<Number>3453453453</Number>
</PhoneNumber>
</Numbers>
</Employee>
Desired Result:
<Employee>
<Id>1</Id>
<Numbers>
<Home>1231231231</Home>
<Office>3453453453</Office>
</Numbers>
</Employee>
PhoneNumber Type can be added dynamically like "GuestRoomPhone" etc, so adding properties for each phone number type is not an option.
You can do this by implementing the IXmlSerializable interface on your classes. This allows you to control how the values are written and read.
public class Employee : IXmlSerializable
{
public int Id { get; set; }
public PhoneNumber[] Numbers { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement("Employee");
reader.ReadStartElement("Id");
Id = reader.ReadContentAsInt();
reader.ReadEndElement(); // Id
reader.ReadStartElement("Numbers");
List<PhoneNumber> numbers = new List<PhoneNumber>();
while (reader.MoveToContent() == XmlNodeType.Element)
{
PhoneNumber num = new PhoneNumber();
num.ReadXml(reader);
numbers.Add(num);
}
Numbers = numbers.ToArray();
reader.ReadEndElement(); // Numbers
reader.ReadEndElement(); // Employee
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartElement("Id");
writer.WriteValue(Id);
writer.WriteEndElement();
writer.WriteStartElement("Numbers");
foreach (PhoneNumber num in Numbers)
{
num.WriteXml(writer);
}
writer.WriteEndElement(); // Numbers
}
}
Similarly for the PhoneNumber class.
public class PhoneNumber : IXmlSerializable
{
public string Type { get; set; }
public string Number { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
while (!reader.IsStartElement())
reader.Read();
Type = reader.Name;
Number = reader.ReadElementContentAsString();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartElement(Type);
writer.WriteString(Number);
writer.WriteEndElement();
}
}

Wrap properties with CData Section - XML Serialization C#

I need to serialize my object in such a way that the properties I want, would get wrapped around CData sections.
I was hoping I could do something like this :
public class Order
{
[JsonProperty]
public int OrderId { get; set; }
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public int Type { get; set; }
[JsonProperty]
public decimal Amount { get; set; }
[JsonProperty]
public DateTime Date { get; set; }
[DataMember]
[JsonProperty]
**[WrapCData]**
public List<Option> ListB { get; set; }
[DataMember]
public List<string> ListC { get; set; }
**[WrapCData]**
public Product Product { get; set; }
}
Is there any attribute or an implementation which could wrap my specific properties around a CData section? Existing StackOverflow answers suggest fiddling with the Entity(Class) itself. This would get really messy.
In the following thread :
How do you serialize a string as CDATA using XmlSerializer?
Philip's answer suggests to make another property and its corresponding CData property. However the property is a string. CreateCDataSection() also takes a string. I need to wrap my custom objects/lists around CDataSections. How can I do that? Any help would be appreciated. Thanks.
Sample XML for the above Order Class:
<Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OrderId>2</OrderId>
<Name>Some Name</Name>
<Type>1</Type>
<Amount>100</Amount>
<Date>2015-12-07T15:10:49.6031106+05:00</Date>
<![CDATA[
<ListB>
<Option>
<OptionValue>OptionValue1</OptionValue>
<OptionName>Option1</OptionName>
</Option>
<Option>
<OptionValue>OptionValue2</OptionValue>
<OptionName>Option2</OptionName>
</Option>
</ListB>
]]>
<ListC>
<string>ListItem1</string>
<string>ListItem2</string>
</ListC>
<![CDATA[
<Product>
<ProductId>1</ProductId>
<Name>ProductName</Name>
<Type>Product Type</Type>
</Product>
]]>
</Order>
With some effort and customization, it possible to get close to what you want, however XmlSerializer will always place the CData nodes at the end of the container element. Your example XML shows the CData nodes between specific nodes of the container element. As long as you don't need this precise control, you can use How do you serialize a string as CDATA using XmlSerializer? to do nested serializations, like so:
public class Order
{
[JsonProperty]
public int OrderId { get; set; }
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public int Type { get; set; }
[JsonProperty]
public decimal Amount { get; set; }
[JsonProperty]
public DateTime Date { get; set; }
[DataMember]
[JsonProperty]
[XmlIgnore] // Do not serialize directly
[XmlWrapCData] // Instead include in CDATA nodes
public List<Option> ListB { get; set; }
[DataMember]
public List<string> ListC { get; set; }
[XmlIgnore] // Do not serialize directly
[XmlWrapCData] // Instead include in CDATA nodes
public Product Product { get; set; }
[XmlText] // NECESSARY TO EMIT CDATA NODES
[IgnoreDataMember]
[JsonIgnore]
public XmlNode[] CDataContent
{
get
{
return XmlWrapCDataHelper.GetCDataContent(this);
}
set
{
XmlWrapCDataHelper.SetCDataContent(this, value);
}
}
}
public class Product
{
public string ProductId { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
public class Option
{
public string OptionValue { get; set; }
public string OptionName { get; set; }
}
Using the following extension methods and custom attribute:
[System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false)]
public class XmlWrapCDataAttribute : Attribute
{
public XmlWrapCDataAttribute() { this.Namespace = string.Empty; }
public XmlWrapCDataAttribute(string name) : this() { this.Name = name; }
public string Name { get; set; }
public string Namespace { get; set; }
}
public static class XmlWrapCDataHelper
{
static Tuple<PropertyInfo, XmlWrapCDataAttribute> [] XmlWrapCDataProperties(Type type)
{
return type.GetProperties()
.Where(p => p.GetGetMethod() != null && p.GetSetMethod() != null)
.Select(p => Tuple.Create(p, p.GetCustomAttribute<XmlWrapCDataAttribute>()))
.Where(p => p.Item2 != null)
.ToArray();
}
public static XmlNode[] GetCDataContent(object obj)
{
var index = new object[0];
var properties = XmlWrapCDataProperties(obj.GetType());
return properties.Select(p => (XmlNode)p.Item1.GetValue(obj, index).GetCData(p.Item2.Name ?? p.Item1.Name, p.Item2.Namespace)).ToArray();
}
public static void SetCDataContent(object obj, XmlNode [] nodes)
{
if (nodes == null || nodes.Length < 1)
return;
var index = new object[0];
var properties = XmlWrapCDataProperties(obj.GetType()).ToDictionary(p => XName.Get(p.Item2.Name ?? p.Item1.Name, p.Item2.Namespace), p => p);
var xml = "<Root>" + String.Concat(nodes.Select(c => c.Value)) + "</Root>";
foreach (var element in XElement.Parse(xml).Elements())
{
Tuple<PropertyInfo, XmlWrapCDataAttribute> pair;
if (properties.TryGetValue(element.Name, out pair))
{
var value = element.Deserialize(pair.Item1.PropertyType, element.Name.LocalName, element.Name.Namespace.NamespaceName);
pair.Item1.SetValue(obj, value, index);
}
}
}
}
public static class XmlSerializationHelper
{
public static XmlCDataSection GetCData(this object obj, string rootName, string rootNamespace)
{
return obj == null ? null : new System.Xml.XmlDocument().CreateCDataSection(obj.GetXml(XmlSerializerFactory.Create(obj.GetType(), rootName, rootNamespace)));
}
public static XCData GetCData(this object obj, XmlSerializer serializer = null)
{
return obj == null ? null : new XCData(obj.GetXml(serializer));
}
public static string GetXml(this object obj, XmlSerializer serializer = null)
{
using (var textWriter = new StringWriter())
{
var ns = new XmlSerializerNamespaces();
ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " ", OmitXmlDeclaration = true }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
(serializer ?? new XmlSerializer(obj.GetType())).Serialize(xmlWriter, obj, ns);
return textWriter.ToString();
}
}
public static object Deserialize(this XContainer element, Type type, string rootName = null, string rootNamespace = null)
{
return element.Deserialize(type, XmlSerializerFactory.Create(type, rootName, rootNamespace));
}
public static object Deserialize(this XContainer element, Type type, XmlSerializer serializer = null)
{
using (var reader = element.CreateReader())
{
return (serializer ?? new XmlSerializer(type)).Deserialize(reader);
}
}
public static T DeserializeXML<T>(this string xmlString, XmlSerializer serializer = null)
{
using (StringReader reader = new StringReader(xmlString))
{
return (T)(serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
}
}
}
public static class XmlSerializerFactory
{
readonly static Dictionary<Tuple<Type, string, string>, XmlSerializer> cache;
readonly static object padlock;
static XmlSerializerFactory()
{
padlock = new object();
cache = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
}
public static XmlSerializer Create(Type serializedType, string rootName, string rootNamespace)
{
if (serializedType == null)
throw new ArgumentNullException();
if (rootName == null && rootNamespace == null)
return new XmlSerializer(serializedType);
lock (padlock)
{
XmlSerializer serializer;
var key = Tuple.Create(serializedType, rootName, rootNamespace);
if (!cache.TryGetValue(key, out serializer))
cache[key] = serializer = new XmlSerializer(serializedType, new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace });
return serializer;
}
}
}
This will parse your provided XML successfully, and in return generate XML that looks like:
<Order>
<OrderId>2</OrderId>
<Name>Some Name</Name>
<Type>1</Type>
<Amount>100</Amount>
<Date>2015-12-07T05:10:49.6031106-05:00</Date>
<ListC>
<string>ListItem1</string>
<string>ListItem2</string>
</ListC><![CDATA[<ListB>
<Option>
<OptionValue>OptionValue1</OptionValue>
<OptionName>Option1</OptionName>
</Option>
<Option>
<OptionValue>OptionValue2</OptionValue>
<OptionName>Option2</OptionName>
</Option>
</ListB>]]><![CDATA[<Product>
<ProductId>1</ProductId>
<Name>ProductName</Name>
<Type>Product Type</Type>
</Product>]]></Order>

Xml Data In WCF Not Serializing Correctly

i have been developing a wcf service with rest. Here is my DataContract that i have defined in the service. This is service will be consumed by the android device users and the data will be get passed in the service method in the form of the xml.
[DataContract(Namespace = "")]
public class Employee
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public List<City> city { get; set; }
}
[DataContract]
public class City
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string CityName { get; set; }
}
And the following is the servicecontract that i have defined
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/SaveData/New")]
void SaveData(Employee emp);
}
Now the implementation code for this service is as follows :
public void SaveData(Employee emp)
{
Employee obj = emp;
DataContractSerializer dcs = new DataContractSerializer(typeof(Employee));
using (Stream stream = new FileStream(#"D:\file.xml", FileMode.Create, FileAccess.Write))
{
using (XmlDictionaryWriter writer =
XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8))
{
writer.WriteStartDocument();
dcs.WriteObject(writer, obj);
}
}
When i send the data in xml format using fiddler it is not getting parsed correctly. Here is what i m passing to the method using fiddler :
<Employee>
<ID>1</ID>
<Name>Nitin Singh</Name>
<City>
<Id>1<Id>
<CityName>New Delhi<CityName>
<City>
</Employee>
the output that it is rendering is as follows : -
<?xml version="1.0" encoding="utf-8"?><Employee xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ID>1</ID><Name>Nitin Singh</Name><city i:nil="true" xmlns:a="http://schemas.datacontract.org/2004/07/SampleService"/></Employee>
I want the city valus should also be present into but it is not happening here. Kindly help me to figure out this. Thanks
XmlSerializer allows you to "flatten" a list into a sequence of identically named elements, like so:
[XmlRoot("Employee", Namespace="")]
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
[XmlElement("City")]
public List<City> City { get; set; }
}
public class City
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string CityName { get; set; }
}
And, to use it:
public static class XmlSerializationHelper
{
public static string GetXml<T>(T obj, XmlSerializer serializer, bool omitStandardNamespaces)
{
using (var textWriter = new StringWriter())
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true; // For cosmetic purposes.
settings.IndentChars = " "; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
{
if (omitStandardNamespaces)
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
serializer.Serialize(xmlWriter, obj, ns);
}
else
{
serializer.Serialize(xmlWriter, obj);
}
}
return textWriter.ToString();
}
}
public static string GetXml<T>(this T obj, bool omitNamespace)
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
return GetXml(obj, serializer, omitNamespace);
}
public static string GetXml<T>(this T obj)
{
return GetXml(obj, false);
}
}
Test code:
var employee = new Employee { Name = "Nitin Singh", ID = 1, City = new[] { new City { CityName = "New Delhi", Id = 1 }, new City { CityName = "Bangalore", Id = 2 } }.ToList() };
var xml = employee.GetXml();
Debug.WriteLine(xml);
For your classes this produces the following XML:
<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>1</ID>
<Name>Nitin Singh</Name>
<City>
<Id>1</Id>
<CityName>New Delhi</CityName>
</City>
<City>
<Id>2</Id>
<CityName>Bangalore</CityName>
</City>
</Employee>
Is that what you want? This writes the XML to a string for testing purposes. Instructions for writing to a file are here: How to: Write Object Data to an XML File.
(You made a couple minor mistakes with your data contract -- public List<City> city should have been public List<City> City and public class City needed [DataContract(Namespace = "")]. However, the resulting list would have been two levels deep.)

XmlSerializer not deserializing int array

I have the following type:
[XmlRoot(Namespace = "http://schemas.datacontract.org/2004/07/MyNamespace")]
public class Location
{
public int Id { get; set; }
public string Name { get; set; }
public Collection<int> DataSourceIds { get; set; }
}
I'm serializing a list of Locations to XML, resulting in the following:
<ArrayOfLocation xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MyNamespace">
<Location>
<DataSourceIds xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d3p1:int>1</d3p1:int>
</DataSourceIds>
<Id>2</Id>
<Name>First</Name>
</Location>
<Location>
<DataSourceIds xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d3p1:int>1</d3p1:int>
<d3p1:int>2</d3p1:int>
<d3p1:int>3</d3p1:int>
<d3p1:int>4</d3p1:int>
</DataSourceIds>
<Id>1</Id>
<Name>Second</Name>
</Location>
</ArrayOfLocation>
I then try to deserialize this XML as follows:
var rootAttribute = new XmlRootAttribute("ArrayOfLocation")
{
Namespace = "http://schemas.datacontract.org/2004/07/MyNamespace"
};
var serializer = new XmlSerializer(typeof(Location[]), rootAttribute);
using (var xmlReader = XmlReader.Create(new StreamReader(response.GetResponseStream())))
{
locations = (Location[])serializer.Deserialize(xmlReader);
}
This returns a list of Location objects, with every property set correctly... except DataSourceIds, which remains empty. Why isn't XmlSerializer deserializing the array of integers?
Since it was serialized with DataContractSerializer, you can deserialize it like so:
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/MyNamespace")]
public class Location
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public Collection<int> DataSourceIds { get; set; }
}
And then use it like:
using (var xmlReader = XmlReader.Create(stream))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(Location[]));
var locations = (Location[])serializer.ReadObject(xmlReader);
Debug.WriteLine(DataContractSerializerHelper.GetXml(locations, serializer)); // Debug check on re-serialization, remove when not needed.
}
The XmlRoot declaration is ignored by DataContractSerializer.
Finally, a utility method for re-serializing to an XML string, for debugging purposes:
public static class DataContractSerializerHelper
{
private static MemoryStream GenerateStreamFromString(string value)
{
return new MemoryStream(Encoding.Unicode.GetBytes(value ?? ""));
}
public static string GetXml<T>(T obj, DataContractSerializer serializer) where T : class
{
using (var textWriter = new StringWriter())
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = " "; // For cosmetic purposes.
using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings))
{
serializer.WriteObject(xmlWriter, obj);
}
return textWriter.ToString();
}
}
public static string GetXml<T>(T obj) where T : class
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
return GetXml(obj, serializer);
}
}

Categories