C# DataContractSerializer - Deserialize list of list - c#

I have problem deserializing list of list in my xml file:
<?xml version="1.0" encoding="UTF-8"?>
<RootLevel><!--Container-->
<ListOfOne><!--List of One -->
<One>
<ListOfTwo> <!--List of Two -->
<Two></Two>
</ListOfTwo>
</One>
</ListOfOne>
</RootLevel>
RootLevel has List of One.
One has List of Two
The first level (ListOfOne) is working file with out any problem, the problem is that the ListOfTwo is not being deserialized
[KnownType(typeof(List<One>))]
[DataContract(Name = "RootLevel", Namespace = "")]
public sealed class RootLevel
{
[DataMember()]
public List<One> ListOfOne { get; set; }
public RootLevel()
{
ListOfOne = new List<One>();
}
}
[DataContract(Name = "One", Namespace = "")]
[KnownType(typeof(List<Two>))]
public sealed class One
{
public One()
{
ListOfTwo = new List<Two>();
}
[OnDeserialized]
internal void OnSerializingMethod(StreamingContext context)
{
ListOfTwo = new List<Two>();
}
[DataMember]
public List<Two> ListOfTwo { get; set; }
}
[DataContract(Name = "Two", Namespace = "")]
public sealed class Two
{}
This is the operation:
using (var fs = new FileStream("path to file", FileMode.Open))
{
using (var reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas()))
{
DataContractSerializer ser = new DataContractSerializer(typeof(RootLevel));
var deserializedPerson = (RootLevel)ser.ReadObject(reader, true);
Assert.IsTrue(deserializedPerson.ListOfOne[0].ListOfTwo.Count > 0);
reader.Close();
fs.Close();
}
}

If you drop this part of the code everything works as expected:
[OnDeserialized]
internal void OnSerializingMethod(StreamingContext context)
ListOfTwo = new List<Two>();
}
If you wish to make sure you always have an empty ListOfTwo change it to:
[OnDeserialized]
internal void OnSerializingMethod(StreamingContext context)
{
if(ListOfTwo == null) {
ListOfTwo = new List<Two>();
}
}
I ran the code with a small modification (not reading from file)
string xml = #"<?xml version=""1.0"" encoding=""UTF-8""?>
<RootLevel> <!--Container-->
<ListOfOne> <!--List of One -->
<One>
<ListOfTwo> <!--List of Two -->
<Two></Two>
</ListOfTwo>
</One>
</ListOfOne>
</RootLevel>";
var stream = new MemoryStream(Encoding.Default.GetBytes(xml));
using (var reader = XmlDictionaryReader
.CreateTextReader(stream,
new XmlDictionaryReaderQuotas()))
{
DataContractSerializer ser = new DataContractSerializer(typeof(RootLevel));
var deserializedPerson = (RootLevel)ser.ReadObject(reader, true);
Assert.IsTrue(deserializedPerson.ListOfOne[0].ListOfTwo.Count > 0);
reader.Close();
}
with this contract change
[DataContract(Name = "One", Namespace = "")]
[KnownType(typeof(List<Two>))]
public sealed class One
{
public One()
{
ListOfTwo = new List<Two>();
}
[OnDeserialized]
internal void OnSerializingMethod(StreamingContext context)
{
if (ListOfTwo == null)
{
ListOfTwo = new List<Two>();
}
}
[DataMember]
public List<Two> ListOfTwo { get; set; }
}
And the Assert is fine, ListOfTwo has one object as expected.

Related

c# xml serialization add namespace to nodes

I have two class : Osoba, test
public class test
{
public string raz { get; set; }
public string dwa { get; set; }
}
public class Osoba
{
public test tehe { get; set; }
}
I also add namespaces to main root and seralize
Osoba ne = new Osoba();
test t1 = new praca2.test();
t1.dwa = "fgfg";
t1.raz = "dfdfdfdf";
ne.tehe = t1;
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("dfs", "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution");
ns.Add("d", "http://schemas.microsoft.com/office/infopath/2009/WSSList/dataFields");
ns.Add("pc", "http://schemas.microsoft.com/office/infopath/2007/PartnerControls");
XmlSerializer xsSubmit = new XmlSerializer(typeof(Osoba));
var xml = #"D:\dupa1.xml";
using (var stream = new FileStream(xml, FileMode.Create))
{
using (XmlWriter writer = XmlWriter.Create(stream))
{
xsSubmit.Serialize(writer, ne,ns);
xml = stream.ToString(); // Your XML
}
}
I get
<?xml version="1.0" encoding="utf-8"?>
<Osoba xmlns:dfs="http://schemas.microsoft.com/office/infopath/2003/dataFormSolution"xmlns:pc="http://schemas.microsoft.com/office/infopath/2007/PartnerControls"xmlns:d="http://schemas.microsoft.com/office/infopath/2009/WSSList/dataFields">
<tehe>
<raz>dfdfdfdf</raz>
<dwa>fgfg</dwa>
</tehe>
</Osoba>
I want add to node namespaces examle:
...
<pc:tehe>
<dfs:raz>dfdfdfdf</dfs:raz>
<dfs:dwa>fgfg</dfs:dwa>
</pc:tehe>
How I can do it?
I try add class atribute which set namespace
[XmlRoot("Node", Namespace="http://flibble")]
but it bad idea
You're almost there you just need to modify your classes slightly:
public class test
{
[XmlElement(Namespace = "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution")]
public string raz { get; set; }
[XmlElement(Namespace = "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution")]
public string dwa { get; set; }
}
public class Osoba
{
[XmlElement(Namespace = "http://schemas.microsoft.com/office/infopath/2007/PartnerControls")]
public test tehe { get; set; }
}
Sample implementation copied mostly from yours:
class Program
{
static void Main(string[] args)
{
Osoba ne = new Osoba();
test t1 = new test();
t1.dwa = "fgfg";
t1.raz = "dfdfdfdf";
ne.tehe = t1;
XmlSerializer xsSubmit = new XmlSerializer(typeof(Osoba));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("dfs", "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution");
ns.Add("pc", "http://schemas.microsoft.com/office/infopath/2007/PartnerControls");
var xml = #"D:\dupa1.xml";
using (var stream = new FileStream(xml, FileMode.Create))
{
using (XmlTextWriter writer = new XmlTextWriter(stream, Encoding.UTF8))
{
xsSubmit.Serialize(writer, ne, ns);
}
}
}
}
You will get this XML:
<?xml version="1.0" encoding="utf-8"?>
<Osoba xmlns:dfs="http://schemas.microsoft.com/office/infopath/2003/dataFormSolution" xmlns:pc="http://schemas.microsoft.com/office/infopath/2007/PartnerControls">
<pc:tehe>
<dfs:raz>dfdfdfdf</dfs:raz>
<dfs:dwa>fgfg</dfs:dwa>
</pc:tehe>
</Osoba>
Hth...
Uisng xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication42
{
class Program
{
static void Main(string[] args)
{
string xmlHeader =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<Osoba xmlns:dfs=\"http://schemas.microsoft.com/office/infopath/2003/dataFormSolution\"" +
" xmlns:pc=\"http://schemas.microsoft.com/office/infopath/2007/PartnerControls\"" +
" xmlns:d=\"http://schemas.microsoft.com/office/infopath/2009/WSSList/dataFields\">" +
"</Osoba>";
XDocument doc = XDocument.Parse(xmlHeader);
XElement osoba = doc.Root;
XNamespace dfsNs = osoba.GetNamespaceOfPrefix("dfs");
XNamespace pcNs = osoba.GetNamespaceOfPrefix("pc");
osoba.Add(new XElement(pcNs + "tehe", new object[] {
new XElement(dfsNs + "raz", "dfdfdfdf"),
new XElement(dfsNs + "dwa", "fgfg")
}));
}
}
}
I think you are new at C#.I suggest you to study C# basics, design patterns and repository pattern.For your requirement you can use below codes
public class Tehe
{
public string Raz { get; set; }
public string Dwa { get; set; }
}
public class TeheRepository
{
private System.Xml.Linq.XDocument xmlDatas;
public TeheRepository(string filePath)
{
xmlDatas = XDocument.Load(filePath);
}
public IEnumerable<Tehe> FindAll()
{
return xmlDatas.Elements().Elements().Select(tehe =>
{
Tehe t = new Tehe();
t.Dwa = tehe.Elements().ElementAt(1).Value;
t.Raz = tehe.Elements().ElementAt(0).Value;
return t;
});
}
}

Deserialize xml to Generic type

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.

My nested Class collection XMLRoot name is not being used when serializing to xml

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.

Deserialize rooted XML elements into an array

I need to save multiple fields from an XML into different arrays.
This is what my XML looks like:
<Content>
<Colours>
<Colour name="Strong Red">
<R>255</R>
<G>0</G>
<B>0</B>
<A>255</A>
</Colour>
</Colours>
<Textures>
<Texture name="Character01">
<Path>Path/Folder</Path>
</Texture>
</Textures>
</Content>
Now everything was working fine when <Colours> was my root and i only added colours into one Array.
Now i want to add Textures and later more through the same XML file, thus moving the root one up to <Content>.
This is what my ColourLoader Class looked like with only Colours and <Colour> being the root of my XML:
[Serializable()]
[XmlRoot("Colours")]
public class ColourLoader
{
[XmlElement("Colour")]
public CustomColour[] Colours;
public static ColourLoader Load(string path)
{
var serializer = new XmlSerializer(typeof(ColourLoader));
using (var stream = new FileStream(path, FileMode.Open))
{
return serializer.Deserialize(stream) as ColourLoader;
}
}
}
My CustomColour class works fine, it uses [XmlElement("R")] etc. to read the values from the XML. It's just that i don't know how to read elements from a nested XML.
I somehow have to skip <Content> and add Colours from <Colours> as root and do the same for <Textures> etc.
I don't want to create multiple XML files as i would like to keep all content managing into one spot and only load an XML file once.
I think this is what you're after. I've also included a method that serializes the object. I find this very helpful in diagnosing idiosyncratic issues with XML Serialization...
Please vote as answer if this solved your issue.
[Serializable()]
[XmlRoot("Content")]
public class Content
{
[XmlArray("Colours")]
[XmlArrayItem("Colour")]
public CustomColour[] Colours { get; set; }
[XmlArray("Textures")]
[XmlArrayItem("Texture")]
public CustomTexture[] Textures { get; set; }
}
[Serializable()]
[XmlRoot("Colour")]
public class CustomColour
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("R")]
public int R { get; set; }
[XmlElement("G")]
public int G { get; set; }
[XmlElement("B")]
public int B { get; set; }
[XmlElement("A")]
public int A { get; set; }
}
[Serializable()]
[XmlRoot("Texture")]
public class CustomTexture
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("Path")]
public string Path { get; set; }
}
public static class ContentLoader
{
public static Content Load(TextReader textReader)
{
var serializer = new XmlSerializer(typeof(Content));
var ret = serializer.Deserialize(textReader) as Content;
return ret;
}
public static void Save(TextWriter textWriter, Content content)
{
var serializer = new XmlSerializer(typeof(Content));
serializer.Serialize(textWriter, content);
}
}
public static void XmlSerializing()
{
var xml = #"<?xml version=""1.0"" encoding=""utf-16""?>
<Content xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<Colours>
<Colour name=""Strong Red"">
<R>255</R>
<G>0</G>
<B>0</B>
<A>255</A>
</Colour>
</Colours>
<Textures>
<Texture name=""Character01"">
<Path>Path/Folder</Path>
</Texture>
</Textures>
</Content>";
var reader = new StringReader(xml);
var content = ContentLoader.Load(reader);
Console.WriteLine("Deserialized version:");
Console.WriteLine(" Colours");
foreach (var colour in content.Colours)
{
Console.WriteLine(" R: {0}, G: {1}, B: {2}, A: {3}", colour.R, colour.G, colour.B, colour.A);
}
Console.WriteLine(" Textures");
foreach (var texture in content.Textures)
{
Console.WriteLine(" Path: {0}", texture.Path);
}
var contentObj = new Content()
{
Colours = new[] { new CustomColour() { Name = "StrongRed", R = 255, G = 0, B = 0, A = 255 } },
Textures = new[] { new CustomTexture() { Name = "Character01", Path = "Path/Folder" } }
};
Console.WriteLine(string.Empty);
Console.WriteLine("Serialized version:");
var writer = new StringWriter();
ContentLoader.Save(writer, contentObj);
Console.WriteLine(writer);
}

DataContractSerializer with Multiple Namespaces

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();
}

Categories

Resources