I am attempting to update the objects that being deserialized. Taking one object, renaming, using parts and adding a new object that will hold the remaining objects. Is there a way in .NET to deserialize the old XML into the new format?
e.g.
Old Structure:
<objectinfo>
<element1></element1>
<element2></element2>
<element3></element3>
<element4></element4>
</objectinfo>
New Structure:
<objinfo>
<element1></element1>
<element2></element2>
</objinfo>
<newobject>
<element3></element3>
<element4></element4>
</newobject>
Note I am using XmlSerializer to deserialize.
Assuming you are using XmlSerializer to deserialize, if your <objectinfo> is an immediate child of the root XML element, then deserializing to some DTO type identical to old type and mapping to the new object manually or via automapper solves the problem quite easily.
If, however, the objects being modified are nested deeply inside an object hierarchy being deserialized then the DTO strategy isn't as convenient because XmlSerializer doesn't offer a general surrogate DTO replacement mechanism. One alternative approach in such situations is to manually handle the unknown elements in an XmlSerializer.UnknownElement event.
To do this in a general way, introduce the following interface and extension methods for XML deserialization:
public interface IUnknownElementHandler
{
void OnUnknownElement(object sender, XmlElementEventArgs e);
}
public static partial class XmlSerializationHelper
{
public static T LoadFromXml<T>(this string xmlString, XmlSerializer serializer = null)
{
serializer = serializer ?? new XmlSerializer(typeof(T)).AddUnknownElementHandler();
using (var reader = new StringReader(xmlString))
return (T)serializer.Deserialize(reader);
}
public static T LoadFromFile<T>(string filename, XmlSerializer serializer = null)
{
serializer = serializer ?? new XmlSerializer(typeof(T)).AddUnknownElementHandler();
using (var reader = new FileStream(filename, FileMode.Open))
return (T)serializer.Deserialize(reader);
}
public static XmlSerializer AddUnknownElementHandler(this XmlSerializer serializer)
{
serializer.UnknownElement += (o, e) =>
{
var handler = e.ObjectBeingDeserialized as IUnknownElementHandler;
if (handler != null)
handler.OnUnknownElement(o, e);
};
return serializer;
}
}
Then, assuming your new data model looks e.g. like this, where Root is the top-level object and ContainerType contains the elements being restructured:
[XmlRoot(ElementName = "Root")]
public class Root
{
public ContainerType ContainerType { get; set; }
}
[XmlRoot(ElementName = "ContainerType")]
public partial class ContainerType
{
[XmlElement(ElementName = "objinfo")]
public Objinfo Objinfo { get; set; }
[XmlElement(ElementName = "newobject")]
public Newobject Newobject { get; set; }
}
[XmlRoot(ElementName = "objinfo")]
public class Objinfo
{
[XmlElement(ElementName = "element1")]
public string Element1 { get; set; }
[XmlElement(ElementName = "element2")]
public string Element2 { get; set; }
}
[XmlRoot(ElementName = "newobject")]
public class Newobject
{
[XmlElement(ElementName = "element3")]
public string Element3 { get; set; }
[XmlElement(ElementName = "element4")]
public string Element4 { get; set; }
}
Add an OnUnknownElement handler to ContainerType as follows:
public partial class ContainerType : IUnknownElementHandler
{
#region IUnknownElementHandler Members
void IUnknownElementHandler.OnUnknownElement(object sender, XmlElementEventArgs e)
{
var container = (ContainerType)e.ObjectBeingDeserialized;
var element1 = e.Element.SelectSingleNode("element1");
var element2 = e.Element.SelectSingleNode("element2");
if (element1 != null || element2 != null)
{
container.Objinfo = container.Objinfo ?? new Objinfo();
if (element1 != null)
container.Objinfo.Element1 = element1.InnerText;
if (element2 != null)
container.Objinfo.Element2 = element2.InnerText;
}
var element3 = e.Element.SelectSingleNode("element3");
var element4 = e.Element.SelectSingleNode("element4");
if (element3 != null || element4 != null)
{
container.Newobject = container.Newobject ?? new Newobject();
if (element3 != null)
container.Newobject.Element3 = element3.InnerText;
if (element4 != null)
container.Newobject.Element4 = element4.InnerText;
}
}
#endregion
}
Then when you deserialize your Root from a file using the LoadFromFile method above:
var root = XmlSerializationHelper.LoadFromFile<Root>(filename);
The obsolete, unknown XML elements will postprocessed by the ContainerType handler.
Demo fiddle here.
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 Model populated and I wish to serlise to an xml document.
Due to naming conventions I have to over ride the class names for my XML document,
This is my Model(s):
[Serializable]
[XmlRoot("preferences")]
public class PreferencesModel
{
[XmlIgnore]
public string MessageToUser { get; set; }
[XmlElement(ElementName = "sectiondivider")]
public List<SectionDivider> SectionDivider { get; set; }
}
[Serializable]
[XmlRoot(ElementName = "sectiondivider")]
public class SectionDivider
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("preference")]
public List<PreferenceModel> PreferenceModel { get; set; }
}
[Serializable]
[XmlRoot("preference")]
public class PreferenceModel
{
[XmlAttribute("type")]
public string Type { get; set; }
public string Name { get; set; }
[XmlAttribute("value")]
public string Value { get; set; }
[XmlElement("options")]
public List<Option> Options { get; set; }
}
this is how I serialize:
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(ObjectToXmlString(obj, includeNameSpace, includeStartDocument, rootAttribute));
return xDoc;
public static string ObjectToXmlString(Object obj, bool includeNameSpace, bool includeStartDocument, XmlRootAttribute rootAttribute)
{
SpecialXmlWriter stWriter = null;
XmlSerializer xmlSerializer = default(XmlSerializer);
string buffer = null;
try
{
if (rootAttribute == null)
{
xmlSerializer = new XmlSerializer(obj.GetType());
}
else
{
xmlSerializer = new XmlSerializer(obj.GetType(), rootAttribute);
}
MemoryStream memStream = new MemoryStream();
StringWriter writer = new StringWriter();
stWriter = new SpecialXmlWriter(memStream, new UTF8Encoding(false), includeStartDocument);
if (!includeNameSpace)
{
System.Xml.Serialization.XmlSerializerNamespaces xs = new XmlSerializerNamespaces();
//To remove namespace and any other inline
//information tag
xs.Add("", "");
xmlSerializer.Serialize(stWriter, obj, xs);
}
else
{
xmlSerializer.Serialize(stWriter, obj);
}
buffer = Encoding.UTF8.GetString(memStream.ToArray());
}
catch (Exception e)
{
string msg = e.Message;
throw;
}
finally
{
if (stWriter != null)
stWriter.Close();
}
return buffer;
}
I call it like this:
XmlDocument preferencesxml = Codec.ObjectToXml(m.SectionDivider,false,
false, new XmlRootAttribute("preferences"));
My m value is:
and my resulting XML is this:
XmlRootAttribute, as the name suggests, only applies to the root element of the XML being serialised.
You need to use XmlTypeAttribute in this context:
[XmlType("sectiondivider")]`
public class SectionDivider
{
//...
}
As an aside, the [Serializable] attribute is not relevant to XmlSerializer - it can be removed unless you need it for some other purpose.
I know Json.net has an attribute [JsonRequired]; are there any XML methods that can do the same thing?.
I don't think there is, so I have made my own way of doing so, but I have stopped at how to handle List and T in reflection.
It's a great help if someone will tell me, thanks.
class Program
{
static void Main(string[] args)
{
string strin = "<TestXML><Fir>f</Fir><TestXML1><TestXML3><For>444</For></TestXML3></TestXML1></TestXML>";
TestXML ttt1 = XmlToModel<TestXML>(strin);
Console.ReadKey();
}
public static T XmlToModel<T>(string xml)
{
StringReader xmlReader = new StringReader(xml);
XmlSerializer xmlSer = new XmlSerializer(typeof(T));
T t = (T)xmlSer.Deserialize(xmlReader);
Type typeT = typeof(T);
IfIsClass<T>(typeT, t);
return t;
}
private static void IfIsClass<T>(Type typeT, T t)
{
foreach (PropertyInfo p in typeT.GetProperties())
{
//here I don't konw how to handle List<T> and T
//if(is List<T> or T)
// IfisClass<T>(typeT,t);
IfIsNull<T>(p, t);
}
}
private static void IfIsNull<T>(PropertyInfo p, T t)
{
var at = p.GetCustomAttribute(typeof(NotNullAttribute));
var pvalue = p.GetValue(t);
if (at != null && string.IsNullOrEmpty(pvalue == null ? "" : pvalue.ToString()))
{
throw new Exception(string.Format("Field {0} not allow null or empty", p.Name));
}
}
}
public class TestXML
{
public string Fir { get; set; }
[NotNull]
public string Sec { get; set; }
[XmlElement("")]
public List<TestXML2> TestXML1 { get; set; }
}
public class TestXML2
{
public string Thir { get; set; }
public TestXML3 TestXML3 { get; set; }
}
public class TestXML3
{
[NotNull]
public string For { get; set; }
}
public class NotNullAttribute : Attribute
{
}
I don't 100% know what you're trying to accomplish with what you're doing right now, and I also don't know which values you don't want to be null, but I do see that there is a much easier way of deserialzing XML into objects (see here for some reference):
[XmlRoot("TestXML")]
public class TestXML
{
[XmlElement("Fir")]
public string Fir { get; set; }
[XmlArray("TestXML1")]
[XmlArrayItem("TestXML3", IsNullable = false)]
public TestXML3[] testxml3 { get; set; }
}
public class TestXML3
{
[XmlElement("For")]
public int For { get; set; }
}
public class Order
{
[XmlElement("number")]
public string Number { get; set; }
}
Once you have that, you can deserialize the xml wherever you want in the same file with:
string xml = #"<TestXML><Fir>f</Fir><TestXML1><TestXML3><For>444</For></TestXML3></TestXML1></TestXML>";
StringReader reader = new StringReader(xml);
XmlSerializer ser = new XmlSerializer(typeof(TestXML));
var data = (TestXML)ser.Deserialize(reader);
Null Checking
Using XML
Basically, you just add IsNullable = false inside the parenthesis of a [XmlArrayItem], for example, to make sure a specific value will not return null (It will skip over it). NOTE: I have only tested this on [XmlArrayItem], I do not know for sure if it will work on other Xml tags...
Using C#
If you really wanted to use C# and throw and exception if it's null (which sort of ruins the point of using [XmlElement] in the first place), you can do something like this instead (see here):
... //same code as before
var data = (TestXML)ser.Deserialize(reader);
//haven't tested this
foreach(obj dataobj in data){
if(dataobj == null) throw new NullReferenceException();
}
Using JSON
If you really want to use something like [JsonRequired], then why not just use it! You can convert the XML data to JSON data using Json.Net (see here):
string xml = #"<TestXML><Fir>f</Fir><TestXML1><TestXML3><For>444</For></TestXML3></TestXML1></TestXML>";
XmlDocument Test = new XMLDocument();
Test.loadXml(xml);
string json = JsonConvert.SerializeXmlNode(Test);
Now, you have your xml data in json format in string json, and you can do whatever you want to it, including setting up a map like I did with the xml and adding [JsonRequired]
someone told me,here is the answer.
public static T XmlToModel<T>(string xml)
{
StringReader xmlReader = new StringReader(xml);
XmlSerializer xmlSer = new XmlSerializer(typeof(T));
T t = (T)xmlSer.Deserialize(xmlReader);
Type typeT = typeof(T);
IfIsClass(t);
return t;
}
private static void IfIsClass(object t)
{
var typeT = t.GetType();
foreach (PropertyInfo p in typeT.GetProperties())
{
if (typeof(System.Collections.ICollection).IsAssignableFrom(p.PropertyType))
{
var vValue = p.GetValue(t, null) as System.Collections.ICollection;
foreach(var item in vValue )
{
IfIsClass(item);
}
}
else
{
IfIsNull(p, p.GetValue(t, null));
}
}
}
private static void IfIsNull(PropertyInfo p, object pvalue)
{
var at = p.GetCustomAttribute(typeof(NotNullAttribute));
if (at != null && string.IsNullOrEmpty(pvalue == null ? "" : pvalue.ToString()))
{
throw new Exception(string.Format("field[{0}]not allow null or empty", p.Name));
}
}
Add a constructor
public TestXML()
{
Sec = "";
}
If I have something like:
public abstract class Animal { }
[XmlRoot]
public class Dog:Animal { }
[XmlRoot]
Public class Cat:Animal { }
How do I go about serializing (and then be able to de-serialize) a Cat object like:
<Cat> </Cat>
and not something like:
<Animal type="Cat"> </Animal>
I've tried using different combinations from the System.Xml.Serialization and System.Runtime.Serialization namespaces, but I can't seem to get it.
I can get a serialized object to look the way I want by specifying the type of the object in the serializer. This works for serialization, but not for de-serialization..because I don't know the type of object in the xml.
One possible solution would be:
public abstract class Animal
{
static Dictionary<String,Type> typeDic;
static Animal()
{
typeDic = new Dictionary<string, Type>();
//Get classes in the same namespace of this object
Type ctype = typeof(Animal);
Type[] types = ctype.Assembly.GetTypes().Where(t => String.Equals(t.Namespace, ctype.Namespace, StringComparison.Ordinal)).ToArray();
//For any XmlRootAttribute objects, add the ElementName and Type to a dictionary
foreach(Type type in types)
{
foreach (XmlRootAttribute xmlRoot in type.GetCustomAttributes(typeof(XmlRootAttribute), false))
{
typeDic.Add(xmlRoot.ElementName, type);
}
}
}
public static Content read(String XML)
{
XmlSerializer s = null;
//check the first element to see what the name is, then create the serializer using the type from the dictionary
XmlReader reader = XmlReader.Create(GenerateStreamFromString(XML));
reader.Read();
if (reader.Name == "xml")
{
while (reader.MoveToContent() != XmlNodeType.Element) { }
}
if (typeDic.ContainsKey(reader.Name))
s = new XmlSerializer(typeDic[reader.Name]);
else
throw new Exception("Unknown Type in read");
return (Content)s.Deserialize(reader);
}
public static string write<T>(T f)
{
XmlSerializer s = new XmlSerializer(typeof(T));
MemoryStream stream = new MemoryStream();
s.Serialize(stream, f);
stream.Position = 0;
return StreamToString(stream);
}
}
[XmlRoot(ElementName="Dog")]
public class Dog:Animal { }
[XmlRoot(ElementName="Cat")]
Public class Cat:Animal { }
I tried this for a long time and could not figure it out either. the only thing i could come up with is using some reflection and generating the doc myself:
class Program
{
static void Main(string[] args)
{
List<Animal> animals = new List<Animal>
{
new Dog{Name = "Ernie", HasFleas = true},
new Cat{ Name = "Bert", Collar = "Blue with a little bell" }
};
XDocument doc = new XDocument();
doc.Declaration = new XDeclaration("1.0","utf-8","true");
doc.Add(new XElement("root", animals.Select(animal => animal.GetElement)));
Console.WriteLine(doc);
}
}
public abstract class Animal
{
[XmlAttribute]
public string Name { get; set; }
public XElement GetElement
{
get
{
Type type = this.GetType();
XElement result = new XElement(type.Name);
foreach (PropertyInfo property in
type.GetProperties().Where(pi=> pi.CustomAttributes.Any(ca=> ca.AttributeType == typeof(System.Xml.Serialization.XmlAttributeAttribute))))
{
result.Add(new XAttribute(property.Name,property.GetValue(this)));
}
foreach (PropertyInfo property in
type.GetProperties().Where(pi => pi.CustomAttributes.Any(ca => ca.AttributeType == typeof(System.Xml.Serialization.XmlElementAttribute))))
{
result.Add(new XElement(property.Name,property.GetValue(this)));
}
return result;
}
}
}
public class Dog : Animal
{
[XmlAttribute]
public bool HasFleas { get; set; }
}
public class Cat : Animal
{
[XmlElement]
public string Collar { get; set; }
}
To deserialize you have to do the other way round so some form of factory.
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();
}