如何将xsi schemalocation添加到root c#object XmlSerializer

时间:2023-01-16 19:37:33

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 following

我正在使用XmlSerializer创建一个表示XML文件的对象,现在我想将一个schemalocation添加到我的xml文件的rootelement中。我可以添加如下命名空间

        XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
        System.IO.FileStream fs = new FileStream(@"C:\test.xml", FileMode.Create);
        TextWriter writer = new StreamWriter(fs, new UTF8Encoding());

        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
        ns.Add("xy","http://www.w3.org/2005/08/addressing");
        ns.Add("xlink","http://www.w3.org/1999/xlink");
        serializer.Serialize(writer, myObject, ns);

But how do i add a xsi:schemalocation attribute to my root element within my c# code. Namespace was added with a simple ns.Add(). I would like to avoid messing around with the xsd.exe generated c# class. Or do i have to edit manually the generated c# class and add some attribute to the root element of my xml?

但是如何在我的c#代码中向我的根元素添加xsi:schemalocation属性。命名空间添加了一个简单的ns.Add()。我想避免搞乱xsd.exe生成的c#类。或者我是否必须手动编辑生成的c#类并将一些属性添加到我的xml的根元素中?

EDIT: I have seen examples where i need to edit my c# manually, but there must be a way to do it in code!! If we are able to add namespaces to our root element, why shouldn't it be possible to add schemalocations?

编辑:我已经看到我需要手动编辑我的c#的例子,但必须有一种方法在代码中执行它!如果我们能够将命名空间添加到根元素中,为什么不能添加schemalocations?

2 个解决方案

#1


9  

Let's assume the following XSD:

我们假设以下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:

xsd.exe生成:

//------------------------------------------------------------------------------
// <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:

要“注入”xsi:schemaLocation,添加一个新类elementA:elementB;注意:

  • System.Xml.Serialization.XmlRootAttribute setup
  • System.Xml.Serialization.XmlRootAttribute设置
  • schemaLocation property setup.
  • schemaLocation属性设置。

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:

您实现自定义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>

#2


14  

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.

XSD.exe生成部分类,因此您可以添加自己的单独的部分类来保存xsi:schemaLocation等字段或属性。

So, adding to @Petru Gardea's sample elementB class, you only need to create another file in your project and add this partial class:

因此,添加到@Petru Gardea的示例elementB类,您只需要在项目中创建另一个文件并添加此分部类:

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.

我遇到了一个问题,默认情况下,xsd.exe没有为生成的文件添加命名空间。当您创建自己的此分部类时,它很可能位于命名空间中。由于 和显式定义的命名空间不匹配,因此partial不起作用。因此,您需要在xsd.exe上使用namespace选项来实际将生成的类放入命名空间。

#1


9  

Let's assume the following XSD:

我们假设以下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:

xsd.exe生成:

//------------------------------------------------------------------------------
// <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:

要“注入”xsi:schemaLocation,添加一个新类elementA:elementB;注意:

  • System.Xml.Serialization.XmlRootAttribute setup
  • System.Xml.Serialization.XmlRootAttribute设置
  • schemaLocation property setup.
  • schemaLocation属性设置。

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:

您实现自定义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>

#2


14  

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.

XSD.exe生成部分类,因此您可以添加自己的单独的部分类来保存xsi:schemaLocation等字段或属性。

So, adding to @Petru Gardea's sample elementB class, you only need to create another file in your project and add this partial class:

因此,添加到@Petru Gardea的示例elementB类,您只需要在项目中创建另一个文件并添加此分部类:

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.

我遇到了一个问题,默认情况下,xsd.exe没有为生成的文件添加命名空间。当您创建自己的此分部类时,它很可能位于命名空间中。由于 和显式定义的命名空间不匹配,因此partial不起作用。因此,您需要在xsd.exe上使用namespace选项来实际将生成的类放入命名空间。