Omitting all xsi and xsd namespaces when serializing an object in .NET? - c#

The code looks like this:
StringBuilder builder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
using (XmlWriter xmlWriter = XmlWriter.Create(builder, settings))
{
XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
s.Serialize(xmlWriter, objectToSerialize);
}
The resulting serialized document includes namespaces, like so:
<message xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns="urn:something">
...
</message>
To remove the xsi and xsd namespaces, I can follow the answer from How to serialize an object to XML without getting xmlns=”…”?.
I want my message tag as <message> (without any namespace attributes). How can I do this?

...
XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("","");
s.Serialize(xmlWriter, objectToSerialize, ns);

This is the 2nd of two answers.
If you want to just strip all namespaces arbitrarily from a document during serialization, you can do this by implementing your own XmlWriter.
The easiest way is to derive from XmlTextWriter and override the StartElement method that emits namespaces. The StartElement method is invoked by the XmlSerializer when emitting any elements, including the root. By overriding the namespace for each element, and replacing it with the empty string, you've stripped the namespaces from the output.
public class NoNamespaceXmlWriter : XmlTextWriter
{
//Provide as many contructors as you need
public NoNamespaceXmlWriter(System.IO.TextWriter output)
: base(output) { Formatting= System.Xml.Formatting.Indented;}
public override void WriteStartDocument () { }
public override void WriteStartElement(string prefix, string localName, string ns)
{
base.WriteStartElement("", localName, "");
}
}
Suppose this is the type:
// explicitly specify a namespace for this type,
// to be used during XML serialization.
[XmlRoot(Namespace="urn:Abracadabra")]
public class MyTypeWithNamespaces
{
// private fields backing the properties
private int _Epoch;
private string _Label;
// explicitly define a distinct namespace for this element
[XmlElement(Namespace="urn:Whoohoo")]
public string Label
{
set { _Label= value; }
get { return _Label; }
}
// this property will be implicitly serialized to XML using the
// member name for the element name, and inheriting the namespace from
// the type.
public int Epoch
{
set { _Epoch= value; }
get { return _Epoch; }
}
}
Here's how you would use such a thing during serialization:
var o2= new MyTypeWithNamespaces { ..intializers.. };
var builder = new System.Text.StringBuilder();
using ( XmlWriter writer = new NoNamespaceXmlWriter(new System.IO.StringWriter(builder)))
{
s2.Serialize(writer, o2, ns2);
}
Console.WriteLine("{0}",builder.ToString());
The XmlTextWriter is sort of broken, though. According to the reference doc, when it writes it does not check for the following:
Invalid characters in attribute and element names.
Unicode characters that do not fit the specified encoding. If the Unicode
characters do not fit the specified
encoding, the XmlTextWriter does not
escape the Unicode characters into
character entities.
Duplicate attributes.
Characters in the DOCTYPE public
identifier or system identifier.
These problems with XmlTextWriter have been around since v1.1 of the .NET Framework, and they will remain, for backward compatibility. If you have no concerns about those problems, then by all means use the XmlTextWriter. But most people would like a bit more reliability.
To get that, while still suppressing namespaces during serialization, instead of deriving from XmlTextWriter, define a concrete implementation of the abstract XmlWriter and its 24 methods.
An example is here:
public class XmlWriterWrapper : XmlWriter
{
protected XmlWriter writer;
public XmlWriterWrapper(XmlWriter baseWriter)
{
this.Writer = baseWriter;
}
public override void Close()
{
this.writer.Close();
}
protected override void Dispose(bool disposing)
{
((IDisposable) this.writer).Dispose();
}
public override void Flush()
{
this.writer.Flush();
}
public override string LookupPrefix(string ns)
{
return this.writer.LookupPrefix(ns);
}
public override void WriteBase64(byte[] buffer, int index, int count)
{
this.writer.WriteBase64(buffer, index, count);
}
public override void WriteCData(string text)
{
this.writer.WriteCData(text);
}
public override void WriteCharEntity(char ch)
{
this.writer.WriteCharEntity(ch);
}
public override void WriteChars(char[] buffer, int index, int count)
{
this.writer.WriteChars(buffer, index, count);
}
public override void WriteComment(string text)
{
this.writer.WriteComment(text);
}
public override void WriteDocType(string name, string pubid, string sysid, string subset)
{
this.writer.WriteDocType(name, pubid, sysid, subset);
}
public override void WriteEndAttribute()
{
this.writer.WriteEndAttribute();
}
public override void WriteEndDocument()
{
this.writer.WriteEndDocument();
}
public override void WriteEndElement()
{
this.writer.WriteEndElement();
}
public override void WriteEntityRef(string name)
{
this.writer.WriteEntityRef(name);
}
public override void WriteFullEndElement()
{
this.writer.WriteFullEndElement();
}
public override void WriteProcessingInstruction(string name, string text)
{
this.writer.WriteProcessingInstruction(name, text);
}
public override void WriteRaw(string data)
{
this.writer.WriteRaw(data);
}
public override void WriteRaw(char[] buffer, int index, int count)
{
this.writer.WriteRaw(buffer, index, count);
}
public override void WriteStartAttribute(string prefix, string localName, string ns)
{
this.writer.WriteStartAttribute(prefix, localName, ns);
}
public override void WriteStartDocument()
{
this.writer.WriteStartDocument();
}
public override void WriteStartDocument(bool standalone)
{
this.writer.WriteStartDocument(standalone);
}
public override void WriteStartElement(string prefix, string localName, string ns)
{
this.writer.WriteStartElement(prefix, localName, ns);
}
public override void WriteString(string text)
{
this.writer.WriteString(text);
}
public override void WriteSurrogateCharEntity(char lowChar, char highChar)
{
this.writer.WriteSurrogateCharEntity(lowChar, highChar);
}
public override void WriteValue(bool value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(DateTime value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(decimal value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(double value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(int value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(long value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(object value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(float value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(string value)
{
this.writer.WriteValue(value);
}
public override void WriteWhitespace(string ws)
{
this.writer.WriteWhitespace(ws);
}
public override XmlWriterSettings Settings
{
get
{
return this.writer.Settings;
}
}
protected XmlWriter Writer
{
get
{
return this.writer;
}
set
{
this.writer = value;
}
}
public override System.Xml.WriteState WriteState
{
get
{
return this.writer.WriteState;
}
}
public override string XmlLang
{
get
{
return this.writer.XmlLang;
}
}
public override System.Xml.XmlSpace XmlSpace
{
get
{
return this.writer.XmlSpace;
}
}
}
Then, provide a derived class that overrides the StartElement method, as before:
public class NamespaceSupressingXmlWriter : XmlWriterWrapper
{
//Provide as many contructors as you need
public NamespaceSupressingXmlWriter(System.IO.TextWriter output)
: base(XmlWriter.Create(output)) { }
public NamespaceSupressingXmlWriter(XmlWriter output)
: base(XmlWriter.Create(output)) { }
public override void WriteStartElement(string prefix, string localName, string ns)
{
base.WriteStartElement("", localName, "");
}
}
And then use this writer like so:
var o2= new MyTypeWithNamespaces { ..intializers.. };
var builder = new System.Text.StringBuilder();
var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
using ( XmlWriter innerWriter = XmlWriter.Create(builder, settings))
using ( XmlWriter writer = new NamespaceSupressingXmlWriter(innerWriter))
{
s2.Serialize(writer, o2, ns2);
}
Console.WriteLine("{0}",builder.ToString());
Credit for this to Oleg Tkachenko.

After reading Microsoft's documentation and several solutions online, I have discovered the solution to this problem. It works with both the built-in XmlSerializer and custom XML serialization via IXmlSerialiazble.
To wit, I'll use the same MyTypeWithNamespaces XML sample that's been used in the answers to this question so far.
[XmlRoot("MyTypeWithNamespaces", Namespace="urn:Abracadabra", IsNullable=false)]
public class MyTypeWithNamespaces
{
// As noted below, per Microsoft's documentation, if the class exposes a public
// member of type XmlSerializerNamespaces decorated with the
// XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
// namespaces during serialization.
public MyTypeWithNamespaces( )
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
// Don't do this!! Microsoft's documentation explicitly says it's not supported.
// It doesn't throw any exceptions, but in my testing, it didn't always work.
// new XmlQualifiedName(string.Empty, string.Empty), // And don't do this:
// new XmlQualifiedName("", "")
// DO THIS:
new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
// Add any other namespaces, with prefixes, here.
});
}
// If you have other constructors, make sure to call the default constructor.
public MyTypeWithNamespaces(string label, int epoch) : this( )
{
this._label = label;
this._epoch = epoch;
}
// An element with a declared namespace different than the namespace
// of the enclosing type.
[XmlElement(Namespace="urn:Whoohoo")]
public string Label
{
get { return this._label; }
set { this._label = value; }
}
private string _label;
// An element whose tag will be the same name as the property name.
// Also, this element will inherit the namespace of the enclosing type.
public int Epoch
{
get { return this._epoch; }
set { this._epoch = value; }
}
private int _epoch;
// Per Microsoft's documentation, you can add some public member that
// returns a XmlSerializerNamespaces object. They use a public field,
// but that's sloppy. So I'll use a private backed-field with a public
// getter property. Also, per the documentation, for this to work with
// the XmlSerializer, decorate it with the XmlNamespaceDeclarations
// attribute.
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;
}
That's all to this class. Now, some objected to having an XmlSerializerNamespaces object somewhere within their classes; but as you can see, I neatly tucked it away in the default constructor and exposed a public property to return the namespaces.
Now, when it comes time to serialize the class, you would use the following code:
MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);
/******
OK, I just figured I could do this to make the code shorter, so I commented out the
below and replaced it with what follows:
// You have to use this constructor in order for the root element to have the right namespaces.
// If you need to do custom serialization of inner objects, you can use a shortened constructor.
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces), new XmlAttributeOverrides(),
new Type[]{}, new XmlRootAttribute("MyTypeWithNamespaces"), "urn:Abracadabra");
******/
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });
// I'll use a MemoryStream as my backing store.
MemoryStream ms = new MemoryStream();
// This is extra! If you want to change the settings for the XmlSerializer, you have to create
// a separate XmlWriterSettings object and use the XmlTextWriter.Create(...) factory method.
// So, in this case, I want to omit the XML declaration.
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Encoding = Encoding.UTF8; // This is probably the default
// You could use the XmlWriterSetting to set indenting and new line options, but the
// XmlTextWriter class has a much easier method to accomplish that.
// The factory method returns a XmlWriter, not a XmlTextWriter, so cast it.
XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);
// Then we can set our indenting options (this is, of course, optional).
xtw.Formatting = Formatting.Indented;
// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);
Once you have done this, you should get the following output:
<MyTypeWithNamespaces>
<Label xmlns="urn:Whoohoo">myLabel</Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
I have successfully used this method in a recent project with a deep hierachy of classes that are serialized to XML for web service calls. Microsoft's documentation is not very clear about what to do with the publicly accesible XmlSerializerNamespaces member once you've created it, and so many think it's useless. But by following their documentation and using it in the manner shown above, you can customize how the XmlSerializer generates XML for your classes without resorting to unsupported behavior or "rolling your own" serialization by implementing IXmlSerializable.
It is my hope that this answer will put to rest, once and for all, how to get rid of the standard xsi and xsd namespaces generated by the XmlSerializer.
UPDATE: I just want to make sure I answered the OP's question about removing all namespaces. My code above will work for this; let me show you how. Now, in the example above, you really can't get rid of all namespaces (because there are two namespaces in use). Somewhere in your XML document, you're going to need to have something like xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo. If the class in the example is part of a larger document, then somewhere above a namespace must be declared for either one of (or both) Abracadbra and Whoohoo. If not, then the element in one or both of the namespaces must be decorated with a prefix of some sort (you can't have two default namespaces, right?). So, for this example, Abracadabra is the defalt namespace. I could inside my MyTypeWithNamespaces class add a namespace prefix for the Whoohoo namespace like so:
public MyTypeWithNamespaces
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
new XmlQualifiedName("w", "urn:Whoohoo")
});
}
Now, in my class definition, I indicated that the <Label/> element is in the namespace "urn:Whoohoo", so I don't need to do anything further. When I now serialize the class using my above serialization code unchanged, this is the output:
<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
<w:Label>myLabel</w:Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
Because <Label> is in a different namespace from the rest of the document, it must, in someway, be "decorated" with a namespace. Notice that there are still no xsi and xsd namespaces.

XmlSerializer sr = new XmlSerializer(objectToSerialize.GetType());
TextWriter xmlWriter = new StreamWriter(filename);
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
sr.Serialize(xmlWriter, objectToSerialize, namespaces);

This is the first of my two answers to the question.
If you want fine control over the namespaces - for example if you want to omit some of them but not others, or if you want to replace one namespace with another, you can do this using XmlAttributeOverrides.
Suppose you have this type definition:
// explicitly specify a namespace for this type,
// to be used during XML serialization.
[XmlRoot(Namespace="urn:Abracadabra")]
public class MyTypeWithNamespaces
{
// private fields backing the properties
private int _Epoch;
private string _Label;
// explicitly define a distinct namespace for this element
[XmlElement(Namespace="urn:Whoohoo")]
public string Label
{
set { _Label= value; }
get { return _Label; }
}
// this property will be implicitly serialized to XML using the
// member name for the element name, and inheriting the namespace from
// the type.
public int Epoch
{
set { _Epoch= value; }
get { return _Epoch; }
}
}
And this serialization pseudo-code:
var o2= new MyTypeWithNamespaces() { ..initializers...};
ns.Add( "", "urn:Abracadabra" );
XmlSerializer s2 = new XmlSerializer(typeof(MyTypeWithNamespaces));
s2.Serialize(System.Console.Out, o2, ns);
You would get something like this XML:
<MyTypeWithNamespaces xmlns="urn:Abracadabra">
<Label xmlns="urn:Whoohoo">Cimsswybclaeqjh</Label>
<Epoch>97</Epoch>
</MyTypeWithNamespaces>
Notice that there is a default namespace on the root element, and there is also a distinct namespace on the "Label" element. These namespaces were dictated by the attributes decorating the type, in the code above.
The Xml Serialization framework in .NET includes the possibility to explicitly override the attributes that decorate the actual code. You do this with the XmlAttributesOverrides class and friends. Suppose I have the same type, and I serialize it this way:
// instantiate the container for all attribute overrides
XmlAttributeOverrides xOver = new XmlAttributeOverrides();
// define a set of XML attributes to apply to the root element
XmlAttributes xAttrs1 = new XmlAttributes();
// define an XmlRoot element (as if [XmlRoot] had decorated the type)
// The namespace in the attribute override is the empty string.
XmlRootAttribute xRoot = new XmlRootAttribute() { Namespace = ""};
// add that XmlRoot element to the container of attributes
xAttrs1.XmlRoot= xRoot;
// add that bunch of attributes to the container holding all overrides
xOver.Add(typeof(MyTypeWithNamespaces), xAttrs1);
// create another set of XML Attributes
XmlAttributes xAttrs2 = new XmlAttributes();
// define an XmlElement attribute, for a type of "String", with no namespace
var xElt = new XmlElementAttribute(typeof(String)) { Namespace = ""};
// add that XmlElement attribute to the 2nd bunch of attributes
xAttrs2.XmlElements.Add(xElt);
// add that bunch of attributes to the container for the type, and
// specifically apply that bunch to the "Label" property on the type.
xOver.Add(typeof(MyTypeWithNamespaces), "Label", xAttrs2);
// instantiate a serializer with the overrides
XmlSerializer s3 = new XmlSerializer(typeof(MyTypeWithNamespaces), xOver);
// serialize
s3.Serialize(System.Console.Out, o2, ns2);
The result looks like this;
<MyTypeWithNamespaces>
<Label>Cimsswybclaeqjh</Label>
<Epoch>97</Epoch>
</MyTypeWithNamespaces>
You have stripped the namespaces.
A logical question is, can you strip all namespaces from arbitrary types during serialization, without going through the explicit overrides? The answer is YES, and how to do it is in my next response.

Related

c# XML Attribute delete [duplicate]

The code looks like this:
StringBuilder builder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
using (XmlWriter xmlWriter = XmlWriter.Create(builder, settings))
{
XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
s.Serialize(xmlWriter, objectToSerialize);
}
The resulting serialized document includes namespaces, like so:
<message xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns="urn:something">
...
</message>
To remove the xsi and xsd namespaces, I can follow the answer from How to serialize an object to XML without getting xmlns=”…”?.
I want my message tag as <message> (without any namespace attributes). How can I do this?
...
XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("","");
s.Serialize(xmlWriter, objectToSerialize, ns);
This is the 2nd of two answers.
If you want to just strip all namespaces arbitrarily from a document during serialization, you can do this by implementing your own XmlWriter.
The easiest way is to derive from XmlTextWriter and override the StartElement method that emits namespaces. The StartElement method is invoked by the XmlSerializer when emitting any elements, including the root. By overriding the namespace for each element, and replacing it with the empty string, you've stripped the namespaces from the output.
public class NoNamespaceXmlWriter : XmlTextWriter
{
//Provide as many contructors as you need
public NoNamespaceXmlWriter(System.IO.TextWriter output)
: base(output) { Formatting= System.Xml.Formatting.Indented;}
public override void WriteStartDocument () { }
public override void WriteStartElement(string prefix, string localName, string ns)
{
base.WriteStartElement("", localName, "");
}
}
Suppose this is the type:
// explicitly specify a namespace for this type,
// to be used during XML serialization.
[XmlRoot(Namespace="urn:Abracadabra")]
public class MyTypeWithNamespaces
{
// private fields backing the properties
private int _Epoch;
private string _Label;
// explicitly define a distinct namespace for this element
[XmlElement(Namespace="urn:Whoohoo")]
public string Label
{
set { _Label= value; }
get { return _Label; }
}
// this property will be implicitly serialized to XML using the
// member name for the element name, and inheriting the namespace from
// the type.
public int Epoch
{
set { _Epoch= value; }
get { return _Epoch; }
}
}
Here's how you would use such a thing during serialization:
var o2= new MyTypeWithNamespaces { ..intializers.. };
var builder = new System.Text.StringBuilder();
using ( XmlWriter writer = new NoNamespaceXmlWriter(new System.IO.StringWriter(builder)))
{
s2.Serialize(writer, o2, ns2);
}
Console.WriteLine("{0}",builder.ToString());
The XmlTextWriter is sort of broken, though. According to the reference doc, when it writes it does not check for the following:
Invalid characters in attribute and element names.
Unicode characters that do not fit the specified encoding. If the Unicode
characters do not fit the specified
encoding, the XmlTextWriter does not
escape the Unicode characters into
character entities.
Duplicate attributes.
Characters in the DOCTYPE public
identifier or system identifier.
These problems with XmlTextWriter have been around since v1.1 of the .NET Framework, and they will remain, for backward compatibility. If you have no concerns about those problems, then by all means use the XmlTextWriter. But most people would like a bit more reliability.
To get that, while still suppressing namespaces during serialization, instead of deriving from XmlTextWriter, define a concrete implementation of the abstract XmlWriter and its 24 methods.
An example is here:
public class XmlWriterWrapper : XmlWriter
{
protected XmlWriter writer;
public XmlWriterWrapper(XmlWriter baseWriter)
{
this.Writer = baseWriter;
}
public override void Close()
{
this.writer.Close();
}
protected override void Dispose(bool disposing)
{
((IDisposable) this.writer).Dispose();
}
public override void Flush()
{
this.writer.Flush();
}
public override string LookupPrefix(string ns)
{
return this.writer.LookupPrefix(ns);
}
public override void WriteBase64(byte[] buffer, int index, int count)
{
this.writer.WriteBase64(buffer, index, count);
}
public override void WriteCData(string text)
{
this.writer.WriteCData(text);
}
public override void WriteCharEntity(char ch)
{
this.writer.WriteCharEntity(ch);
}
public override void WriteChars(char[] buffer, int index, int count)
{
this.writer.WriteChars(buffer, index, count);
}
public override void WriteComment(string text)
{
this.writer.WriteComment(text);
}
public override void WriteDocType(string name, string pubid, string sysid, string subset)
{
this.writer.WriteDocType(name, pubid, sysid, subset);
}
public override void WriteEndAttribute()
{
this.writer.WriteEndAttribute();
}
public override void WriteEndDocument()
{
this.writer.WriteEndDocument();
}
public override void WriteEndElement()
{
this.writer.WriteEndElement();
}
public override void WriteEntityRef(string name)
{
this.writer.WriteEntityRef(name);
}
public override void WriteFullEndElement()
{
this.writer.WriteFullEndElement();
}
public override void WriteProcessingInstruction(string name, string text)
{
this.writer.WriteProcessingInstruction(name, text);
}
public override void WriteRaw(string data)
{
this.writer.WriteRaw(data);
}
public override void WriteRaw(char[] buffer, int index, int count)
{
this.writer.WriteRaw(buffer, index, count);
}
public override void WriteStartAttribute(string prefix, string localName, string ns)
{
this.writer.WriteStartAttribute(prefix, localName, ns);
}
public override void WriteStartDocument()
{
this.writer.WriteStartDocument();
}
public override void WriteStartDocument(bool standalone)
{
this.writer.WriteStartDocument(standalone);
}
public override void WriteStartElement(string prefix, string localName, string ns)
{
this.writer.WriteStartElement(prefix, localName, ns);
}
public override void WriteString(string text)
{
this.writer.WriteString(text);
}
public override void WriteSurrogateCharEntity(char lowChar, char highChar)
{
this.writer.WriteSurrogateCharEntity(lowChar, highChar);
}
public override void WriteValue(bool value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(DateTime value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(decimal value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(double value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(int value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(long value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(object value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(float value)
{
this.writer.WriteValue(value);
}
public override void WriteValue(string value)
{
this.writer.WriteValue(value);
}
public override void WriteWhitespace(string ws)
{
this.writer.WriteWhitespace(ws);
}
public override XmlWriterSettings Settings
{
get
{
return this.writer.Settings;
}
}
protected XmlWriter Writer
{
get
{
return this.writer;
}
set
{
this.writer = value;
}
}
public override System.Xml.WriteState WriteState
{
get
{
return this.writer.WriteState;
}
}
public override string XmlLang
{
get
{
return this.writer.XmlLang;
}
}
public override System.Xml.XmlSpace XmlSpace
{
get
{
return this.writer.XmlSpace;
}
}
}
Then, provide a derived class that overrides the StartElement method, as before:
public class NamespaceSupressingXmlWriter : XmlWriterWrapper
{
//Provide as many contructors as you need
public NamespaceSupressingXmlWriter(System.IO.TextWriter output)
: base(XmlWriter.Create(output)) { }
public NamespaceSupressingXmlWriter(XmlWriter output)
: base(XmlWriter.Create(output)) { }
public override void WriteStartElement(string prefix, string localName, string ns)
{
base.WriteStartElement("", localName, "");
}
}
And then use this writer like so:
var o2= new MyTypeWithNamespaces { ..intializers.. };
var builder = new System.Text.StringBuilder();
var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
using ( XmlWriter innerWriter = XmlWriter.Create(builder, settings))
using ( XmlWriter writer = new NamespaceSupressingXmlWriter(innerWriter))
{
s2.Serialize(writer, o2, ns2);
}
Console.WriteLine("{0}",builder.ToString());
Credit for this to Oleg Tkachenko.
After reading Microsoft's documentation and several solutions online, I have discovered the solution to this problem. It works with both the built-in XmlSerializer and custom XML serialization via IXmlSerialiazble.
To wit, I'll use the same MyTypeWithNamespaces XML sample that's been used in the answers to this question so far.
[XmlRoot("MyTypeWithNamespaces", Namespace="urn:Abracadabra", IsNullable=false)]
public class MyTypeWithNamespaces
{
// As noted below, per Microsoft's documentation, if the class exposes a public
// member of type XmlSerializerNamespaces decorated with the
// XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
// namespaces during serialization.
public MyTypeWithNamespaces( )
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
// Don't do this!! Microsoft's documentation explicitly says it's not supported.
// It doesn't throw any exceptions, but in my testing, it didn't always work.
// new XmlQualifiedName(string.Empty, string.Empty), // And don't do this:
// new XmlQualifiedName("", "")
// DO THIS:
new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
// Add any other namespaces, with prefixes, here.
});
}
// If you have other constructors, make sure to call the default constructor.
public MyTypeWithNamespaces(string label, int epoch) : this( )
{
this._label = label;
this._epoch = epoch;
}
// An element with a declared namespace different than the namespace
// of the enclosing type.
[XmlElement(Namespace="urn:Whoohoo")]
public string Label
{
get { return this._label; }
set { this._label = value; }
}
private string _label;
// An element whose tag will be the same name as the property name.
// Also, this element will inherit the namespace of the enclosing type.
public int Epoch
{
get { return this._epoch; }
set { this._epoch = value; }
}
private int _epoch;
// Per Microsoft's documentation, you can add some public member that
// returns a XmlSerializerNamespaces object. They use a public field,
// but that's sloppy. So I'll use a private backed-field with a public
// getter property. Also, per the documentation, for this to work with
// the XmlSerializer, decorate it with the XmlNamespaceDeclarations
// attribute.
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;
}
That's all to this class. Now, some objected to having an XmlSerializerNamespaces object somewhere within their classes; but as you can see, I neatly tucked it away in the default constructor and exposed a public property to return the namespaces.
Now, when it comes time to serialize the class, you would use the following code:
MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);
/******
OK, I just figured I could do this to make the code shorter, so I commented out the
below and replaced it with what follows:
// You have to use this constructor in order for the root element to have the right namespaces.
// If you need to do custom serialization of inner objects, you can use a shortened constructor.
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces), new XmlAttributeOverrides(),
new Type[]{}, new XmlRootAttribute("MyTypeWithNamespaces"), "urn:Abracadabra");
******/
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });
// I'll use a MemoryStream as my backing store.
MemoryStream ms = new MemoryStream();
// This is extra! If you want to change the settings for the XmlSerializer, you have to create
// a separate XmlWriterSettings object and use the XmlTextWriter.Create(...) factory method.
// So, in this case, I want to omit the XML declaration.
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Encoding = Encoding.UTF8; // This is probably the default
// You could use the XmlWriterSetting to set indenting and new line options, but the
// XmlTextWriter class has a much easier method to accomplish that.
// The factory method returns a XmlWriter, not a XmlTextWriter, so cast it.
XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);
// Then we can set our indenting options (this is, of course, optional).
xtw.Formatting = Formatting.Indented;
// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);
Once you have done this, you should get the following output:
<MyTypeWithNamespaces>
<Label xmlns="urn:Whoohoo">myLabel</Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
I have successfully used this method in a recent project with a deep hierachy of classes that are serialized to XML for web service calls. Microsoft's documentation is not very clear about what to do with the publicly accesible XmlSerializerNamespaces member once you've created it, and so many think it's useless. But by following their documentation and using it in the manner shown above, you can customize how the XmlSerializer generates XML for your classes without resorting to unsupported behavior or "rolling your own" serialization by implementing IXmlSerializable.
It is my hope that this answer will put to rest, once and for all, how to get rid of the standard xsi and xsd namespaces generated by the XmlSerializer.
UPDATE: I just want to make sure I answered the OP's question about removing all namespaces. My code above will work for this; let me show you how. Now, in the example above, you really can't get rid of all namespaces (because there are two namespaces in use). Somewhere in your XML document, you're going to need to have something like xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo. If the class in the example is part of a larger document, then somewhere above a namespace must be declared for either one of (or both) Abracadbra and Whoohoo. If not, then the element in one or both of the namespaces must be decorated with a prefix of some sort (you can't have two default namespaces, right?). So, for this example, Abracadabra is the defalt namespace. I could inside my MyTypeWithNamespaces class add a namespace prefix for the Whoohoo namespace like so:
public MyTypeWithNamespaces
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
new XmlQualifiedName("w", "urn:Whoohoo")
});
}
Now, in my class definition, I indicated that the <Label/> element is in the namespace "urn:Whoohoo", so I don't need to do anything further. When I now serialize the class using my above serialization code unchanged, this is the output:
<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
<w:Label>myLabel</w:Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
Because <Label> is in a different namespace from the rest of the document, it must, in someway, be "decorated" with a namespace. Notice that there are still no xsi and xsd namespaces.
XmlSerializer sr = new XmlSerializer(objectToSerialize.GetType());
TextWriter xmlWriter = new StreamWriter(filename);
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
sr.Serialize(xmlWriter, objectToSerialize, namespaces);
This is the first of my two answers to the question.
If you want fine control over the namespaces - for example if you want to omit some of them but not others, or if you want to replace one namespace with another, you can do this using XmlAttributeOverrides.
Suppose you have this type definition:
// explicitly specify a namespace for this type,
// to be used during XML serialization.
[XmlRoot(Namespace="urn:Abracadabra")]
public class MyTypeWithNamespaces
{
// private fields backing the properties
private int _Epoch;
private string _Label;
// explicitly define a distinct namespace for this element
[XmlElement(Namespace="urn:Whoohoo")]
public string Label
{
set { _Label= value; }
get { return _Label; }
}
// this property will be implicitly serialized to XML using the
// member name for the element name, and inheriting the namespace from
// the type.
public int Epoch
{
set { _Epoch= value; }
get { return _Epoch; }
}
}
And this serialization pseudo-code:
var o2= new MyTypeWithNamespaces() { ..initializers...};
ns.Add( "", "urn:Abracadabra" );
XmlSerializer s2 = new XmlSerializer(typeof(MyTypeWithNamespaces));
s2.Serialize(System.Console.Out, o2, ns);
You would get something like this XML:
<MyTypeWithNamespaces xmlns="urn:Abracadabra">
<Label xmlns="urn:Whoohoo">Cimsswybclaeqjh</Label>
<Epoch>97</Epoch>
</MyTypeWithNamespaces>
Notice that there is a default namespace on the root element, and there is also a distinct namespace on the "Label" element. These namespaces were dictated by the attributes decorating the type, in the code above.
The Xml Serialization framework in .NET includes the possibility to explicitly override the attributes that decorate the actual code. You do this with the XmlAttributesOverrides class and friends. Suppose I have the same type, and I serialize it this way:
// instantiate the container for all attribute overrides
XmlAttributeOverrides xOver = new XmlAttributeOverrides();
// define a set of XML attributes to apply to the root element
XmlAttributes xAttrs1 = new XmlAttributes();
// define an XmlRoot element (as if [XmlRoot] had decorated the type)
// The namespace in the attribute override is the empty string.
XmlRootAttribute xRoot = new XmlRootAttribute() { Namespace = ""};
// add that XmlRoot element to the container of attributes
xAttrs1.XmlRoot= xRoot;
// add that bunch of attributes to the container holding all overrides
xOver.Add(typeof(MyTypeWithNamespaces), xAttrs1);
// create another set of XML Attributes
XmlAttributes xAttrs2 = new XmlAttributes();
// define an XmlElement attribute, for a type of "String", with no namespace
var xElt = new XmlElementAttribute(typeof(String)) { Namespace = ""};
// add that XmlElement attribute to the 2nd bunch of attributes
xAttrs2.XmlElements.Add(xElt);
// add that bunch of attributes to the container for the type, and
// specifically apply that bunch to the "Label" property on the type.
xOver.Add(typeof(MyTypeWithNamespaces), "Label", xAttrs2);
// instantiate a serializer with the overrides
XmlSerializer s3 = new XmlSerializer(typeof(MyTypeWithNamespaces), xOver);
// serialize
s3.Serialize(System.Console.Out, o2, ns2);
The result looks like this;
<MyTypeWithNamespaces>
<Label>Cimsswybclaeqjh</Label>
<Epoch>97</Epoch>
</MyTypeWithNamespaces>
You have stripped the namespaces.
A logical question is, can you strip all namespaces from arbitrary types during serialization, without going through the explicit overrides? The answer is YES, and how to do it is in my next response.

Deserializing collection of types implementing IXmlSerializable runs forever

I have a class implementing IXmlSerializable. This class contains a few properties. Serializing and Deserializing a single instance of the class works fine. But in case of collection of the class, Serialization works fine but Deserialization runs forever. Here is a code snippet. I am using .Net 4.6.2.
public class MyClass : IXmlSerializable
{
public int A { get; set; }
public int B { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
this.A = Convert.ToInt32(reader.GetAttribute("A"));
this.B = Convert.ToInt32(reader.GetAttribute("B"));
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("A", this.A.ToString());
writer.WriteAttributeString("B", this.B.ToString());
}
}
class Program
{
static void Main(string[] args)
{
var instance = new MyClass { A = 1, B = 2 };
Serialize(instance);
instance = Deserialize<MyClass>();//works fine
var list = new List<MyClass> { new MyClass { A = 10, B = 20 } };
Serialize(list);
list = Deserialize<List<MyClass>>();//runs forever
}
private static void Serialize(object o)
{
XmlSerializer ser = new XmlSerializer(o.GetType());
using (TextWriter writer = new StreamWriter("xml.xml", false, Encoding.UTF8))
{
ser.Serialize(writer, o);
}
}
private static T Deserialize<T>()
{
XmlSerializer ser = new XmlSerializer(typeof(T));
using (TextReader reader = new StreamReader("xml.xml"))
{
return (T)ser.Deserialize(reader);
}
}
}
Your problem is that, as explained in the documentation, ReadXml() must consume its wrapper element as well as its contents:
The ReadXml method must reconstitute your object using the information that was written by the WriteXml method.
When this method is called, the reader is positioned on the start tag that wraps the information for your type. That is, directly on the start tag that indicates the beginning of a serialized object. When this method returns, it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.
MyClass.ReadXml() is not doing this, which causes an infinite loop when the MyClass object is not serialized as the root element. Instead, your MyClass must look something like this:
public class MyClass : IXmlSerializable
{
public int A { get; set; }
public int B { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
/*
* https://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.readxml.aspx
*
* When this method is called, the reader is positioned at the start of the element that wraps the information for your type.
* That is, just before the start tag that indicates the beginning of a serialized object. When this method returns,
* it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method,
* the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these
* positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.
*/
var isEmptyElement = reader.IsEmptyElement;
this.A = XmlConvert.ToInt32(reader.GetAttribute("A"));
this.B = XmlConvert.ToInt32(reader.GetAttribute("B"));
reader.ReadStartElement();
if (!isEmptyElement)
{
reader.ReadEndElement();
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("A", XmlConvert.ToString(this.A));
writer.WriteAttributeString("B", XmlConvert.ToString(this.B));
}
}
Now your <MyClass> element is very simple with no nested or optional elements. For more complex custom serializations there are a couple of strategies you could adopt to guarantee that your ReadXml() method reads exactly as much as it should, no more and no less.
Firstly, you could call XNode.ReadFrom() to load the current element into an XElement. This requires a bit more memory than parsing directly from an XmlReader but is much easier to work with:
public class MyClass : IXmlSerializable
{
public int A { get; set; }
public int B { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
var element = (XElement)XNode.ReadFrom(reader);
this.A = (int)element.Attribute("A");
this.B = (int)element.Attribute("B");
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("A", XmlConvert.ToString(this.A));
writer.WriteAttributeString("B", XmlConvert.ToString(this.B));
}
}
Secondly, you could use XmlReader.ReadSubtree() to ensure the required XML content is consumed:
public class MyClass : IXmlSerializable
{
public int A { get; set; }
public int B { get; set; }
public XmlSchema GetSchema()
{
return null;
}
protected virtual void ReadXmlSubtree(XmlReader reader)
{
this.A = XmlConvert.ToInt32(reader.GetAttribute("A"));
this.B = XmlConvert.ToInt32(reader.GetAttribute("B"));
}
public void ReadXml(XmlReader reader)
{
// Consume all child nodes of the current element using ReadSubtree()
using (var subReader = reader.ReadSubtree())
{
subReader.MoveToContent();
ReadXmlSubtree(subReader);
}
reader.Read(); // Consume the end element itself.
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("A", XmlConvert.ToString(this.A));
writer.WriteAttributeString("B", XmlConvert.ToString(this.B));
}
}
A few final notes:
Be sure to handle both <MyClass /> and <MyClass></MyClass>. These two forms are semantically identical and a sending system could chose either.
Prefer the methods from the XmlConvert class to convert primitives from and to XML. Doing so handles internationalization correctly.
Be sure to test with and without indentation. Sometimes a ReadXml() method will consume an extra XML node but the bug will be hidden when indentation is enabled -- as it is the whitespace node that gets eaten.
For further reading see How to Implement IXmlSerializable Correctly.

Need to overwrite XMLWriter's method

I need to overwrite the XMLWriter's method "WriteElementString" to not write the element if the value is empty, the code bellow didn't work, tried override and new keywords but it still goes to the framework method.
public static void WriteElementString(this XmlWriter writer,
string localName,
string value)
{
if (!string.IsNullOrWhiteSpace(value))
{
writer.WriteStartElement(localName);
writer.WriteString(value);
writer.WriteEndElement();
}
}
The answer was close but correct solution is:
public abstract class MyWriter : XmlWriter
{
private readonly XmlWriter writer;
public Boolean skipEmptyValues;
public MyWriter(XmlWriter writer)
{
if (writer == null) throw new ArgumentNullException("Writer");
this.writer = writer;
}
public new void WriteElementString(string localName, string value)
{
if (string.IsNullOrWhiteSpace(value) && skipEmptyValues)
{
return;
}
else
{
writer.WriteElementString(localName, value);
}
}
}
You need to create an object that decorates XmlWriter to achieve what you are trying to do. More on the Decorator Pattern
public class MyXmlWriter : XmlWriter
{
private readonly XmlWriter writer;
public MyXmlWriter(XmlWriter writer)
{
if (writer == null) throw new ArgumentNullException("writer");
this.writer = writer;
}
// This will not be a polymorphic call
public new void WriteElementString(string localName, string value)
{
if (string.IsNullOrWhiteSpace(value)) return;
this.writer.WriteElementString(localName, value);
}
// the rest of the XmlWriter methods will need to be implemented using Decorator Pattern
// i.e.
public override void Close()
{
this.writer.Close();
}
...
}
Using the above object in LinqPad
var xmlBuilder = new StringBuilder();
var xmlSettings = new XmlWriterSettings
{
Indent = true
};
using (var writer = XmlWriter.Create(xmlBuilder, xmlSettings))
using (var myWriter = new MyXmlWriter(writer))
{
// must use myWriter here in order for the desired implementation to be called
// if you pass myWriter to another method must pass it as MyXmlWriter
// in order for the desired implementation to be called
myWriter.WriteStartElement("Root");
myWriter.WriteElementString("Included", "Hey I made it");
myWriter.WriteElementString("NotIncluded", "");
}
xmlBuilder.ToString().Dump();
Output:
<?xml version="1.0" encoding="utf-16"?>
<Root>
<Included>Hey I made it</Included>
</Root>
What you are trying to do, is to override a method using an extension method which is not what they are intended to do. See the Binding Extension Methods at Compile Time section on the Extension Methods MSDN Page The compiler will always resolve WriteElementString to the instance implemented by XmlWriter. You would need to manually call your extension method XmlWriterExtensions.WriteElementString(writer, localName, value); in order for your code to execute as you have it.

Custom Xml Serialization breaks list functionality

Ok, so I've got a type:
public class MonitorConfiguration
{
private string m_sourcePath;
private string m_targetPath;
public string TargetPath
{
get { return m_targetPath; }
set { m_targetPath = value; }
}
public string SourcePath
{
get { return m_sourcePath; }
set { m_sourcePath = value; }
}
//need a parameterless constructor, just for serialization
private MonitorConfiguration()
{
}
public MonitorConfiguration(string source, string target)
{
m_sourcePath = source;
m_targetPath = target;
}
}
When I serialise and deserialise a list of these, like this
XmlSerializer xs = new XmlSerializer(typeof(List<MonitorConfiguration>));
using (Stream isfStreamOut = isf.OpenFile("Test1.xml", FileMode.Create))
{
xs.Serialize(isfStreamOut, monitoringPaths);
}
using (Stream isfStreamIn = isf.OpenFile("Test1.xml", FileMode.Open))
{
monitoringPaths = xs.Deserialize(isfStreamIn) as List<MonitorConfiguration>;
}
everything works fine.
However, I really want to hide the public setters of the attributes. This prevents them from being serialised by the XML serialiser. So, I implement my own, like this:
Change the class declaration to this:public class MonitorConfiguration : IXmlSerializable
and add these:
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
//make sure we read everything
while (reader.Read())
{
//find the first element we care about...
if (reader.Name == "SourcePath")
{
m_sourcePath = reader.ReadElementString("SourcePath");
m_targetPath = reader.ReadElementString("TargetPath");
// return;
}
}
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteElementString("SourcePath", m_sourcePath);
writer.WriteElementString("TargetPath", m_targetPath);
}
This seems to work, however, I only ever get the first item from the list out, all the others are forgotten. I've tried with and without the return that's currently commented out. What am I doing wrong here?
It should be noted that this is just a snippet code that illustrates the problem; I'm limited to which XML serialisation technology I'm using my an eternal mechanic.
This CodeProject article explains how to get around a few pitfalls when working with IXmlSerializable.
Specifically, you probably need to call reader.ReadEndElement(); when you've found all your elements in ReadXml (see the section How to Implement ReadXml? in the article).

Generics + XML Serialization + Custom Objects

I'm trying out Generics and I had this (not so) great idea of creating an XMLSerializer class. The code I pieced together is below:
public class Persist<T>
{
private string _path;
public Persist(string path) {
this._path = path;
}
public void save(T objectToSave)
{
XmlSerializer s = new XmlSerializer(typeof(T));
TextWriter w = new StreamWriter(this._path);
try { s.Serialize(w, objectToSave); }
catch (InvalidDataException e) { throw e; }
w.Close(); w.Dispose();
}
public T load()
{
XmlSerializer s = new XmlSerializer(typeof(T));
TextReader r = new StreamReader(this._path);
T obj;
try { obj = (T)s.Deserialize(r); }
catch (InvalidDataException e) { throw e; }
r.Close(); r.Dispose();
return obj;
}
}
Here's the problem: It works fine on Persist<List<string>> or Persist<List<int>> but not on Persist<List<userObject>> or any other custom (but serializable) objects. userObject itself is just a class with two {get;set;} properties, which I have serialized before.
I'm not sure if the problems on my Persist class (generics), XML Serialization code, or somewhere else :( Help is very much appreciated~
Edit:
code for userObject
public class userObject
{
public userObject(string id, string name)
{
this.id = id;
this.name = name;
}
public string id { get;private set; }
public string name { get;set; }
}
Looks to me like your code should just work - even though it does have a few flaws.
EDIT: Your userObject class isn't serializable. Xml serialization only works on types with a public, parameterless constructor - the current class won't work. Also, you should really rewrite your code to avoid explicit calls to .Close() or .Dispose() and instead prefer using where possible - as is, you might get random file locking if at any point during serialization an error occurs and your method terminates by exception - and thus doesn't call .Dispose().
Personally, I tend to use a just-for-serialization object hierarchy that's just a container for data stored in xml and avoids any behavior - particularly side effects. Then you can use a handly little base class that makes this simple.
What I use in my projects is the following:
public class XmlSerializableBase<T> where T : XmlSerializableBase<T>
{
static XmlSerializer serializer = new XmlSerializer(typeof(T));
public static T Deserialize(XmlReader from) { return (T)serializer.Deserialize(from); }
public void SerializeTo(Stream s) { serializer.Serialize(s, this); }
public void SerializeTo(TextWriter w) { serializer.Serialize(w, this); }
public void SerializeTo(XmlWriter xw) { serializer.Serialize(xw, this); }
}
...which caches the serializer in a static object, and simplifies usage (no generic type-paramenters needed at call-locations.
Real-life classes using it:
public class ArtistTopTracks {
public string name;
public string mbid;//always empty
public long reach;
public string url;
}
[XmlRoot("mostknowntracks")]
public class ApiArtistTopTracks : XmlSerializableBase<ApiArtistTopTracks> {
[XmlAttribute]
public string artist;
[XmlElement("track")]
public ArtistTopTracks[] track;
}
Sample serialization calls:
using (var xmlReader = XmlReader.Create([...]))
return ApiArtistTopTracks.Deserialize(xmlReader);
//[...]
ApiArtistTopTracks toptracks = [...];
toptracks.SerializeTo(Console.Out);
There can be a number of reasons why your code fails: This text is particularly helpful when having issues: Troubleshooting Common Problems with the XmlSerializer . Maybe you have some type hierarchy in your user objects and the serializer does not know about it?

Categories

Resources