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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15809868/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 13:57:46  来源:igfitidea点击:

How to add xsi schemalocation to root c # object XmlSerializer

xmlxml-serializationxsdxml-namespaces

提问by Gero

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 文件的对象,现在我想向我的 xml 文件的根元素添加架构位置。我可以添加如下命名空间

        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:schemalocationattribute 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?

但是我如何xsi:schemalocation在我的 c# 代码中向我的根元素添加一个属性。命名空间添加了一个简单的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# 的例子,但必须有一种方法可以在代码中做到这一点!!如果我们能够为我们的根元素添加命名空间,为什么不能添加模式位置?

采纳答案by Petru Gardea

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:schemaLocationadd a new class, elementA : elementB; notice:

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

  • System.Xml.Serialization.XmlRootAttribute setup
  • schemaLocationproperty setup.
  • System.Xml.Serialization.XmlRootAttribute 设置
  • 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>

回答by ToddK

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 不会向生成的文件添加命名空间。当您创建自己的这个分部类时,它很可能位于命名空间中。由于 < default namespace> 和显式定义的命名空间不匹配, partial 将不起作用。因此,您需要使用 xsd.exe 上的命名空间选项将生成的类实际放入命名空间中。