Here is my two classes: the class Characteristic and Definition :
[DataContract]
public class Characteristic
{
[DataMember]
public Definition Definition { get; set; }
}
[Serializable]
public class Definition
{
[XmlAttribute]
public int id;
[XmlAttribute]
public stringName;
}
and this is my implementation :
Characteristic lstChars = new Characteristic()
{
Definition = new Definition()
{
id = Dimension.ID,
name = Dimension.Name
}
};
I get this result:
<Characteristic>
<Definition>
<id>6</id>
<name>ACTIVITY</name>
</Definition>
And my objectif is to get this result:
<Characteristic>
<Definition id="6" Name= "ACTIVITY" />
In order to serialize it, you can use the following ways:
To serialize it into a string:
string result;
using (var writer = new StringWriter())
{
new XmlSerializer(typeof(Characteristic)).Serialize(writer, lstChars);
result = writer.ToString();
}
To serialize and store it on a file:
using (var writer = new StreamWriter(xmlFilePath))
{
new XmlSerializer(typeof(Characteristic)).Serialize(writer, lstChars);
}
Related
I need class which takes two parameter
1- Type ==> class name
2- xml string ==> xml document in string format.
now following class convert xml to object which is all working fine but I need final version as generic type
Converter class
public static partial class XMLPrasing
{
public static Object ObjectToXML(string xml, Type objectType)
{
StringReader strReader = null;
XmlSerializer serializer = null;
XmlTextReader xmlReader = null;
Object obj = null;
try
{
strReader = new StringReader(xml);
serializer = new XmlSerializer(objectType);
xmlReader = new XmlTextReader(strReader);
obj = serializer.Deserialize(xmlReader);
}
catch (Exception exp)
{
//Handle Exception Code
var s = "d";
}
finally
{
if (xmlReader != null)
{
xmlReader.Close();
}
if (strReader != null)
{
strReader.Close();
}
}
return obj;
}
}
as Example class
[Serializable]
[XmlRoot("Genders")]
public class Gender
{
[XmlElement("Genders")]
public List<GenderListWrap> GenderListWrap = new List<GenderListWrap>();
}
public class GenderListWrap
{
[XmlAttribute("list")]
public string ListTag { get; set; }
[XmlElement("Item")]
public List<Item> GenderList = new List<Item>();
}
public class Item
{
[XmlElement("CODE")]
public string Code { get; set; }
[XmlElement("DESCRIPTION")]
public string Description { get; set; }
}
//
<Genders><Genders list=\"1\">
<Item>
<CODE>M</CODE>
<DESCRIPTION>Male</DESCRIPTION></Item>
<Item>
<CODE>F</CODE>
<DESCRIPTION>Female</DESCRIPTION>
</Item></Genders>
</Genders>
Please check my solution if it will help you.
Below is Method, which converts xml to generic Class.
public static T GetValue<T>(String value)
{
StringReader strReader = null;
XmlSerializer serializer = null;
XmlTextReader xmlReader = null;
Object obj = null;
try
{
strReader = new StringReader(value);
serializer = new XmlSerializer(typeof(T));
xmlReader = new XmlTextReader(strReader);
obj = serializer.Deserialize(xmlReader);
}
catch (Exception exp)
{
}
finally
{
if (xmlReader != null)
{
xmlReader.Close();
}
if (strReader != null)
{
strReader.Close();
}
}
return (T)Convert.ChangeType(obj, typeof(T));
}
How to call that method :
Req objreq = new Req();
objreq = GetValue(strXmlData);
Req is Generic Class . strXmlData is xml string .
Request sample xml :
<?xml version="1.0"?>
<Req>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description>
</book>
</Req>
Request Class :
[System.SerializableAttribute()]
public partial class Req {
[System.Xml.Serialization.XmlElementAttribute("book")]
public catalogBook[] book { get; set; }
}
[System.SerializableAttribute()]
public partial class catalogBook {
public string author { get; set; }
public string title { get; set; }
public string genre { get; set; }
public decimal price { get; set; }
public System.DateTime publish_date { get; set; }
public string description { get; set; }
public string id { get; set; }
}
In my case it is working perfectly ,hope it will work in your example .
Thanks .
If you want to serialize and deserialize an object with xml this is the easiest way:
The class we want to serialize and deserialize:
[Serializable]
public class Person()
{
public int Id {get;set;}
public string Name {get;set;}
public DateTime Birth {get;set;}
}
Save an object to xml:
public static void SaveObjectToXml<T>(T obj, string file) where T : class, new()
{
if (string.IsNullOrWhiteSpace(file))
throw new ArgumentNullException("File is empty");
var serializer = new XmlSerializer(typeof(T));
using (Stream fileStream = new FileStream(file, FileMode.Create))
{
using (XmlWriter xmlWriter = new XmlTextWriter(file, Encoding.UTF8))
{
serializer.Serialize(xmlWriter, obj);
}
}
}
Load an object from xml:
public static T LoadObjectFromXml<T>(string file) where T : class, new()
{
if (string.IsNullOrWhiteSpace(file))
throw new ArgumentNullException("File is empty");
if (File.Exists(file))
{
XmlSerializer deserializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StreamReader(file))
{
obj = deserializer.Deserialize(reader) as T;
}
}
}
How to use this methods with our Person class:
Person testPerson = new Person {Id = 1, Name="TestPerson", Birth = DateTime.Now};
SaveObjectToXml(testPerson, string "C:\\MyFolder");
//Do some other stuff
//Oh now we need to load the object
var myPerson = LoadObjectFromXml<Person>("C:\\MyFolder");
Console.WriteLine(myPerson.Name); //TestPerson
The Save and Load Methods are working with every object which class is [Serializable] and which contains public empty constructor.
I have a problem about deserialize an array. Becouse array elements can be of various types. You can see the example:
<?xml version="1.0" encoding="UTF-8"?><export xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://zakupki.gov.ru/oos/export/1" xmlns:oos="http://zakupki.gov.ru/oos/types/1">
<notificationZK>
... item 1 data
</notificationZK>
<notificationZK>
... item 2 data
</notificationZK>
<notificationFF>
... item 3 data
</notificationFF>
</export>
All elements extends notificationType
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationSZType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationPOType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationZKType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationEFType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationOKType))]
public partial class notificationType
{
...
So the question is how can I get the collection of notificationType elements from my XML file? I think I cant do something like
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlArray("")] // ???? what I need write here?
[XmlArrayItem("", typeof(notificationType))] // ??? and here?
public notificationType[] notification { get; set; }
}
Regards!
ADDED-------------
So. I make this:
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationSZType", Type = typeof(notificationSZType))]
[XmlElement("notificationPOType", Type = typeof(notificationPOType))]
[XmlElement("notificationZKType", Type = typeof(notificationZKType))]
[XmlElement("notificationEFType", Type = typeof(notificationEFType))]
[XmlElement("notificationOKType", Type = typeof(notificationOKType))]
public notificationType[] notification { get; set; }
}
class Program
{
static void Main(string[] args)
{
NotificationCollection collection = null;
string path = #"E:\notification.xml";
XmlSerializer serializer = new XmlSerializer(typeof(notificationType));
StreamReader reader = new StreamReader(path);
collection = (NotificationCollection) serializer.Deserialize(reader);
reader.Close();
}
}
but have System.InvalidOperationException was unhandled while serializer.Deserialize(reader);
Message=<export xmlns='http://zakupki.gov.ru/oos/export/1'> not expected.
What im doing wrong?
How about moving the type declarations into the collection?
[XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationZK", typeof(NotificationTypeZK))]
[XmlElement("notificationFF", typeof(NotificationTypeFF))]
public List<NotificationType> Notifications { get; set; }
}
public class NotificationType
{
}
public class NotificationTypeZK : NotificationType { }
public class NotificationTypeFF : NotificationType { }
static void Main(string[] args)
{
var data = #"<export><notificationZK /><notificationZK /><notificationFF /></export>";
var serializer = new XmlSerializer(typeof(NotificationCollection));
using (var reader = new StringReader(data))
{
var notifications = serializer.Deserialize(reader);
}
}
This should do the job
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationSZType", Type = typeof(notificationSZType))]
[XmlElement("notificationPOType", Type = typeof(notificationPOType))]
[XmlElement("notificationZKType", Type = typeof(notificationZKType))]
[XmlElement("notificationEFType", Type = typeof(notificationEFType))]
[XmlElement("notificationOKType", Type = typeof(notificationOKType))]
public notificationType[] notification { get; set; }
}
This question is interesting for me as well. I wrote the simplified app to achieve what you ask for:
[Serializable]
[XmlInclude(typeof(ItemA))]
[XmlInclude(typeof(ItemB))]
public class BaseItem
{
public bool Value { get; set; }
}
[Serializable]
public class ItemA : BaseItem
{
public string Text { get; set; }
}
[Serializable]
public class ItemB : BaseItem
{
public int Number { get; set; }
}
[Serializable]
public class ItemsArray
{
public BaseItem[] Items { get; set; }
}
class Program
{
static void Main(string[] args)
{
var array = new ItemsArray
{
Items = new BaseItem[]
{
new ItemA { Value = true, Text = "Test" },
new ItemB { Value = false, Number = 7 }
}
};
ItemsArray output;
using (var stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(ItemsArray));
serializer.Serialize(stream, array);
stream.Position = 0;
output = (ItemsArray)serializer.Deserialize(stream);
}
}
}
After deserialization we get exactly what we serialized. The XML inside stream looks like:
<?xml version="1.0"?>
<ItemsArray xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<BaseItem xsi:type="ItemA">
<Value>true</Value>
<Text>Test</Text>
</BaseItem>
<BaseItem xsi:type="ItemB">
<Value>false</Value>
<Number>7</Number>
</BaseItem>
</Items>
</ItemsArray>
As was mentioned in other answer, you can't use different tags inside XML array. However, it's still possible to store different types. Serializer does this by using xsi:type attribute.
In order to solve your problem you probably need to use a bit another scheme of XML.
I have this existing XML file serving as a template with NO data, just simple nodes... here's a sample:
<?xml version="1.0" encoding="utf-8" ?>
<catalog>
<cd>
<title />
<artist />
<country />
<company />
<price />
<year />
</cd>
</catalog>
Now I have created a similar class for it.
public class Cd
{
public string Title { get; set; }
public string Artist { get; set; }
public string Country { get; set; }
public string Company { get; set; }
public string Price { get; set; }
public string Year { get; set; }
}
The purpose is this:
Put values on the properties of the var cd = new Cd(); object
Get the existing XML file (template) then pass the values in it (like mapping the object to the existing XML)
Transform the XML template(with values) into XSLT to become HTML
I think that's all.
How to properly do this?
Thanks a lot!
You can use serialization to achieve (1) and (2)
[Serializable]
public class Cd
{
public string Title { get; set; }
public string Artist { get; set; }
public string Country { get; set; }
public string Company { get; set; }
public string Price { get; set; }
public string Year { get; set; }
}
now in order to create an xml from an object use:
public static string SerializeObject<T>(this T obj)
{
var ms = new MemoryStream();
var xs = new XmlSerializer(obj.GetType());
var xmlTextWriter = new XmlTextWriter(ms, Encoding.UTF8);
xs.Serialize(xmlTextWriter, obj);
string serializedObject = new UTF8Encoding().GetString((((MemoryStream)xmlTextWriter.BaseStream).ToArray()));
return serializedObject;
}
in order to create an object from XML use:
public static T DeserializeObject<T>(this string xml)
{
if (xml == null)
throw new ArgumentNullException("xml");
var xs = new XmlSerializer(typeof(T));
var ms = new MemoryStream(new UTF8Encoding().GetBytes(xml));
try
{
new XmlTextWriter(ms, Encoding.UTF8);
return (T)xs.Deserialize(ms);
}
catch
{
return default(T);
}
finally
{
ms.Close();
}
}
I would create class:
class catalog
{
public CD cd {get;set;}
}
Here is serialization and deserealization helper:
public class Xml
{
public static string Serialize<T>(T value) where T : class
{
if (value == null)
{
return string.Empty;
}
var xmlSerializer = new XmlSerializer(typeof(T));
var stringWriter = new StringWriter();
using (var xmlWriter = XmlWriter.Create(stringWriter))
{
xmlSerializer.Serialize(xmlWriter, value);
return stringWriter.ToString();
}
}
public static T Deserialize<T>(string xml)
{
var serializer = new XmlSerializer(typeof(T));
T result;
using (TextReader reader = new StringReader(xml))
{
result = (T) serializer.Deserialize(reader);
}
return result;
}
}
Simply call:
catalog catalogObject = Xml.Deserialize<catalog>(xmlCatalogString);
I suspect you also will need to put some attributes on properties like XmlElement(ElementName = "title") because it is case sensitive.
MSDN
If I have the following:
public class A
{
public B b {get;set;}
}
public class B
{
public string Name {get;set;}
public string Address {get;set;
}
what I want is the xml as:
<A Name="some data" Address="address..." />
So I am trying to flatten my referenced object as attributes.
Is this possible with an XmlSerializer?
yeah, you can do this by using the IXmlSerializable interface:
[Serializable]
public class MyClass : IXmlSerializable
{
public MySubClass SubClass { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartAttribute("Name");
writer.WriteString(SubClass.Name);
writer.WriteEndAttribute();
writer.WriteStartAttribute("Phone");
writer.WriteString(SubClass.Phone);
writer.WriteEndAttribute();
}
}
[Serializable]
public class MySubClass
{
public string Name { get; set; }
public string Phone { get; set; }
}
and then call it like this
var serializer = new XmlSerializer(typeof(MyClass));
using (var writer = new StringWriter())
{
var myClass = new MyClass() {SubClass = new MySubClass() {Name = "Test", Phone = "1234"}};
serializer.Serialize(writer, myClass);
string xml = writer.ToString();
}
this is the xml result:
<?xml version="1.0" encoding="utf-16"?>
<MyClass Name="Test" Phone="1234" />
see msdn too: http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
or you could just specify the attributes that #Morpheus named ;)
I am using a DataContractSerializer to serialize an object to XML. The main object is SecurityHolding with the namespace "http://personaltrading.test.com/" and contains a property called Amount that's a class with the namespace "http://core.test.com". When I serialize this to XML I get the following:
<ArrayOfSecurityHolding xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://personaltrading.test.com/">
<SecurityHolding>
<Amount xmlns:d3p1="http://core.test.com/">
<d3p1:Amount>1.05</d3p1:Amount>
<d3p1:CurrencyCode>USD</d3p1:CurrencyCode>
</Amount>
<BrokerageID>0</BrokerageID>
<BrokerageName i:nil="true" />
<RecordID>3681</RecordID>
</SecurityHolding></ArrayOfSecurityHolding>
Is there anyway I can control the d3p1 prefix? Am I doing something wrong or should I be doing something else?
Firstly, the choice of namespace alias should make no difference to a well-formed parser.
But; does it have to be DataContractSerializer? With XmlSerializer, you can use the overload of Serialize that accepts a XmlSerializerNamespaces. This allows you to pick and choose the namespaces and aliases that you use.
Ultimately; DataContractSerializer is not intended to give full xml control; that isn't its aim. If you want strict xml control, XmlSerializer is a better choice, even if it is older (and has some nuances/foibles of its own).
Full example:
using System;
using System.Xml.Serialization;
public class Amount
{
public const string CoreNamespace = "http://core.test.com/";
[XmlElement("Amount", Namespace=CoreNamespace)]
public decimal Value { get; set; }
[XmlElement("CurrencyCode", Namespace = CoreNamespace)]
public string Currency { get; set; }
}
[XmlType("SecurityHolding", Namespace = SecurityHolding.TradingNamespace)]
public class SecurityHolding
{
public const string TradingNamespace = "http://personaltrading.test.com/";
[XmlElement("Amount", Namespace = Amount.CoreNamespace)]
public Amount Amount { get; set; }
public int BrokerageId { get; set; }
public string BrokerageName { get; set; }
public int RecordId { get; set; }
}
static class Program
{
static void Main()
{
var data = new[] {
new SecurityHolding {
Amount = new Amount {
Value = 1.05M,
Currency = "USD"
},
BrokerageId = 0,
BrokerageName = null,
RecordId = 3681
}
};
var ser = new XmlSerializer(data.GetType(),
new XmlRootAttribute("ArrayOfSecurityHolding") { Namespace = SecurityHolding.TradingNamespace});
var ns = new XmlSerializerNamespaces();
ns.Add("foo", Amount.CoreNamespace);
ser.Serialize(Console.Out, data, ns);
}
}
Output:
<ArrayOfSecurityHolding xmlns:foo="http://core.test.com/" xmlns="http://personaltrading.test.com/">
<SecurityHolding>
<foo:Amount>
<foo:Amount>1.05</foo:Amount>
<foo:CurrencyCode>USD</foo:CurrencyCode>
</foo:Amount>
<BrokerageId>0</BrokerageId>
<RecordId>3681</RecordId>
</SecurityHolding>
</ArrayOfSecurityHolding>
I have solved this problem slightly differently to Marc that can be implemented in a base class.
Create a new attribute to define the additional XML namespaces that you will use in your data contract.
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public sealed class NamespaceAttribute : Attribute
{
public NamespaceAttribute()
{
}
public NamespaceAttribute(string prefix, string uri)
{
Prefix = prefix;
Uri = uri;
}
public string Prefix { get; set; }
public string Uri { get; set; }
}
Add the attribute to your data contracts.
[DataContract(Name = "SomeObject", Namespace = "http://schemas.domain.com/namespace/")]
[Namespace(Prefix = "a", Uri = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
[Namespace(Prefix = "wm", Uri = "http://schemas.datacontract.org/2004/07/System.Windows.Media")]
public class SomeObject : SerializableObject
{
private IList<Color> colors;
[DataMember]
[DisplayName("Colors")]
public IList<Colors> Colors
{
get { return colors; }
set { colours = value; }
}
}
Then in your Save method, use reflection to get the attributes and then write them to the file.
public static void Save(SerializableObject o, string filename)
{
using (Stream outputStream = new FileStream(filename, FileMode.Create, FileAccess.Write))
{
if (outputStream == null)
throw new ArgumentNullException("Must have valid output stream");
if (outputStream.CanWrite == false)
throw new ArgumentException("Cannot write to output stream");
object[] attributes;
attributes = o.GetType().GetCustomAttributes(typeof(NamespaceAttribute), true);
XmlWriterSettings writerSettings = new XmlWriterSettings();
writerSettings.Indent = true;
writerSettings.NewLineOnAttributes = true;
using (XmlWriter w = XmlWriter.Create(outputStream, writerSettings))
{
DataContractSerializer s = new DataContractSerializer(o.GetType());
s.WriteStartObject(w, o);
foreach (NamespaceAttribute ns in attributes)
w.WriteAttributeString("xmlns", ns.Prefix, null, ns.Uri);
// content
s.WriteObjectContent(w, o);
s.WriteEndObject(w);
}
}
}
I have struggled with this problem also. The solution I present below is not optimal IMHO but it works. Like Marc Gravell above, I suggest using XmlSerializer.
The trick is to add a field to your class that returns a XmlSerializerNamespaces object. This field must be decorated with a XmlNamespaceDeclarations attribute. In the constructor of your class, add namespaces as shown in the example below. In the xml below note that the root element is prefixed correctly as well as the someString element.
More info on XmlSerializerNamespaces
Schemas reference
[XmlRoot(Namespace="http://STPMonitor.myDomain.com")]
public class CFMessage : IQueueMessage<CFQueueItem>
{
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces xmlns;
[XmlAttribute("schemaLocation", Namespace=System.Xml.Schema.XmlSchema.InstanceNamespace)]
public string schemaLocation = "http://STPMonitor.myDomain.com/schemas/CFMessage.xsd";
[XmlAttribute("type")]
public string Type { get; set; }
[XmlAttribute("username")]
public string UserName { get; set; }
[XmlAttribute("somestring", Namespace = "http://someURI.com")]
public string SomeString = "Hello World";
public List<CFQueueItem> QueueItems { get; set; }
public CFMessage()
{
xmlns = new XmlSerializerNamespaces();
xmlns.Add("myDomain", "http://STPMonitor.myDomain.com");
xmlns.Add("xyz", "http://someURI.com");
}
}
<?xml version="1.0" encoding="utf-16"?>
<myDomain:CFMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xyz="http://someURI.com"
xsi:schemaLocation="http://STPMonitor.myDomain.com/schemas/CFMessage.xsd"
xyz:somestring="Hello World" type="JOIN" username="SJ-3-3008-1"
xmlns:myDomain="http://STPMonitor.myDomain.com" />
Add "http://www.w3.org/2001/XMLSchema" namespace by:
private static string DataContractSerialize(object obj)
{
StringWriter sw = new StringWriter();
DataContractSerializer serializer = new DataContractSerializer(obj.GetType());
using (XmlTextWriter xw = new XmlTextWriter(sw))
{
//serializer.WriteObject(xw, obj);
//
// Insert namespace for C# types
serializer.WriteStartObject(xw, obj);
xw.WriteAttributeString("xmlns", "x", null, "http://www.w3.org/2001/XMLSchema");
serializer.WriteObjectContent(xw, obj);
serializer.WriteEndObject(xw);
}
StringBuilder buffer = sw.GetStringBuilder();
return buffer.ToString();
}