I am using XmlSerializer to create an object representing an XML file and now i want to add a schemalocation to the rootelement of my xml file. I can add namespaces like the
The XSD.exe generates partial classes, so you can add your own separate partial class to hold things like xsi:schemaLocation as fields or properties.
So, adding to @Petru Gardea's sample elementB class, you only need to create another file in your project and add this partial class:
public partial class elementB
{
[XmlAttributeAttribute("schemaLocation", Namespace="http://www.w3.org/2001/XMLSchema-instance")]
public string xsiSchemaLocation = "http://www.acme.com/xml/OrderXML-1-0.xsd";
}
There is one gotcha that I ran into doing this and that was by default xsd.exe does not add a namespace to the generated file(s). When you create this partial class of your own, it will most likely be in a namespace. Since <default namespace> and an explicitly defined namespace do not match, partial won't work. So, you need to use the namespace option on xsd.exe to actually get the generated classes into your namespace.
Let's assume the following XSD:
<?xml version="1.0" encoding="utf-8" ?>
<!-- XML Schema generated by QTAssistant/XSD Module (http://www.paschidev.com) -->
<xsd:schema targetNamespace="http://tempuri.org/XMLSchema.xsd" xmlns="http://tempuri.org/XMLSchema.xsd" elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="elementB">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="FirstName" type="xsd:string"/>
<xsd:element name="LastName" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
There are two ways at least to do it. The first one relies on inheritance and how you can play with the serializer annotations.
xsd.exe generates this:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.18034
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System.Xml.Serialization;
//
// This source code was auto-generated by xsd, Version=4.0.30319.1.
//
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/XMLSchema.xsd")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/XMLSchema.xsd", IsNullable=false)]
public partial class elementB {
private string firstNameField;
private string lastNameField;
/// <remarks/>
public string FirstName {
get {
return this.firstNameField;
}
set {
this.firstNameField = value;
}
}
/// <remarks/>
public string LastName {
get {
return this.lastNameField;
}
set {
this.lastNameField = value;
}
}
}
To "inject" the xsi:schemaLocation
add a new class, elementA : elementB
; notice:
schemaLocation
property setup.Test program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Xml;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
elementB b = new elementB();
b.FirstName = "P";
b.LastName = "G";
XmlSerializer ser = new XmlSerializer(typeof(elementB));
StringBuilder sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true }))
{
ser.Serialize(writer, b);
}
Console.WriteLine(sb.ToString());
elementA a = new elementA();
a.FirstName = "P";
a.LastName = "G";
a.schemaLocation = "http://tempuri.org/XMLSchema.xsd me";
ser = new XmlSerializer(typeof(elementA));
sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true }))
{
ser.Serialize(writer, a);
}
Console.WriteLine(sb.ToString());
}
}
}
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://tempuri.org/XMLSchema.xsd")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://tempuri.org/XMLSchema.xsd", ElementName = "elementB", IsNullable = false)]
public partial class elementA : elementB
{
private string torefField;
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute(Form = System.Xml.Schema.XmlSchemaForm.Qualified, Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string schemaLocation
{
get
{
return this.torefField;
}
set
{
this.torefField = value;
}
}
}
Generates the expected result:
<?xml version="1.0" encoding="utf-16"?>
<elementB xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/XMLSchema.xsd">
<FirstName>P</FirstName>
<LastName>G</LastName>
</elementB>
<?xml version="1.0" encoding="utf-16"?>
<elementB xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://tempuri.org/XMLSchema.xsd me" xmlns="http://tempuri.org/XMLSchema.xsd">
<FirstName>Petru</FirstName>
<LastName>Gardea</LastName>
</elementB>
The second way relies on a custom writer that will inject what you want, wherever you want it (assuming the appropriate logic).
You implement a custom XmlWriter:
class MyXmlWriter : XmlWriter
{
XmlWriter _writer;
bool _docElement = true;
public string SchemaLocation { get; set; }
public string NoNamespaceSchemaLocation { get; set; }
public MyXmlWriter(XmlWriter writer)
{
_writer = writer;
}
(other methods omitted)
public override void WriteStartElement(string prefix, string localName, string ns)
{
_writer.WriteStartElement(prefix, localName, ns);
if (_docElement)
{
if (!string.IsNullOrEmpty(SchemaLocation))
{
_writer.WriteAttributeString("xsi", "schemaLocation", "http://www.w3.org/2001/XMLSchema-instance", SchemaLocation);
}
if (!string.IsNullOrEmpty(NoNamespaceSchemaLocation))
{
_writer.WriteAttributeString("xsi", "noNamesapceSchemaLocation", "http://www.w3.org/2001/XMLSchema-instance", NoNamespaceSchemaLocation);
}
_docElement = false;
}
}
(other methods omitted)
}
A modified test program:
static void Main(string[] args)
{
elementB b = new elementB();
b.FirstName = "P";
b.LastName = "G";
XmlSerializer ser = new XmlSerializer(typeof(elementB));
StringBuilder sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true }))
{
ser.Serialize(writer, b);
}
Console.WriteLine(sb.ToString());
sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true }))
{
MyXmlWriter newWriter = new MyXmlWriter(writer) { SchemaLocation = "http://tempuri.org/XMLSchema.xsd me" };
ser.Serialize(newWriter, b);
}
Console.WriteLine(sb.ToString());
}
The result is the same...
<?xml version="1.0" encoding="utf-16"?>
<elementB xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/XMLSchema.xsd">
<FirstName>P</FirstName>
<LastName>G</LastName>
</elementB>
<?xml version="1.0" encoding="utf-16"?>
<elementB xsi:schemaLocation="http://tempuri.org/XMLSchema.xsd me" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd">
<FirstName>P</FirstName>
<LastName>G</LastName>
</elementB>