web-dev-qa-db-ja.com

xsiスキーマロケーションをルートcに追加する方法#オブジェクトXmlSerializer

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);
_

しかし、c#コード内のルート要素に_xsi:schemalocation_属性を追加するにはどうすればよいですか?名前空間は単純なns.Add()で追加されました。 xsd.exeで生成されたc#クラスをいじり回さないようにしたいと思います。または、生成されたc#クラスを手動で編集し、xmlのルート要素に属性を追加する必要がありますか?

EDIT: c#を手動で編集する必要がある例を見てきましたが、コードでそれを行う方法が必要です!!ルート要素に名前空間を追加できるのであれば、スキーマロケーションを追加できないのはなぜですか?

13
Gero

次の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>

少なくともそれを行うには2つの方法があります。 1つ目は、継承と、シリアライザーアノテーションを操作する方法に依存しています。

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

xsi:schemaLocationを「注入」するには、新しいクラスelementA : elementBを追加します。通知:

  • System.Xml.Serialization.XmlRootAttributeセットアップ
  • schemaLocationプロパティの設定。

テストプログラム:

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

期待される結果を生成します。

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

2番目の方法は、(適切なロジックを前提として)必要な場所に必要なものを挿入するカスタムライターに依存しています。

カスタム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)

}

変更されたテストプログラム:

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

結果は同じです...

<?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>
13
Petru Gardea

XSD.exeは部分クラスを生成するため、独自の個別の部分クラスを追加して、xsi:schemaLocationなどをフィールドまたはプロパティとして保持できます。

したがって、@ 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";
}

私がこれを行うことに遭遇した1つの落とし穴があります、そしてそれはデフォルトでxsd.exeが生成されたファイルに名前空間を追加しませんでした。この部分クラスを独自に作成すると、ほとんどの場合、名前空間に含まれます。 <デフォルトの名前空間>と明示的に定義された名前空間が一致しないため、partialは機能しません。したがって、xsd.exeの名前空間オプションを使用して、生成されたクラスを実際に名前空間に取り込む必要があります。

21
ToddK