XmlSerializer:不要なxsiおよびxsd名前空間を削除


132

ルート要素にデフォルトの名前空間を書き込まないようにXmlSerializerを構成する方法はありますか?

私が得るものはこれです:

<?xml ...>
<rootelement xmlns:xsi="..." xmlns:xsd="...">
</rootelement>

両方のxmlns宣言を削除したいと思います。

の重複xmlns =”…”を取得せずにオブジェクトをXMLにシリアル化する方法?

回答:


63

デイブは、.NETオブジェクトをシリアル化するときにすべてのxsiおよびxsd名前空間省略することに対する私の答えを繰り返すように求めたので、この投稿を更新し、前述のリンクからここで私の答えを繰り返しました。この回答で使用されている例は、他の質問で使用されているものと同じです。以下はそのままコピーされます。


Microsoftのドキュメントといくつかの解決策をオンラインで読んだ後、この問題の解決策を見つけました。これは、XmlSerializerを介した組み込みXMLシリアル化とカスタムXMLシリアル化の両方で機能しますIXmlSerialiazble

つまり、MyTypeWithNamespacesこれまでこの質問への回答で使用されていたものと同じXMLサンプルを使用します。

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

このクラスはこれですべてです。さて、一部XmlSerializerNamespacesはクラス内のどこかにオブジェクトを持つことに反対しました。しかし、ご覧のとおり、私はそれをデフォルトのコンストラクターにうまく収め、名前空間を返すパブリックプロパティを公開しました。

ここで、クラスをシリアル化するときに、次のコードを使用します。

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

これを実行すると、次の出力が得られます。

<MyTypeWithNamespaces>
    <Label xmlns="urn:Whoohoo">myLabel</Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

私はこのメソッドを、Webサービス呼び出しのためにXMLにシリアル化されるクラスの深い階層を持つ最近のプロジェクトで正常に使用しました。Microsoftのドキュメントは、XmlSerializerNamespacesいったん作成した公にアクセス可能なメンバーをどうするかについてあまり明確ではなく、多くの人はそれを役に立たないと考えています。しかし、それらのドキュメントに従い、上記の方法でそれを使用することにより、サポートされていない動作に頼ったり、を実装することにより「独自のロール」シリアル化に頼ったりすることなく、XmlSerializerがクラスのXMLを生成する方法をカスタマイズできますIXmlSerializable

この答えが、によって生成された標準xsixsd名前空間を取り除く方法を一時停止することを願っていますXmlSerializer

更新:OPのすべての名前空間の削除に関する質問に確実に回答したいだけです。上記の私のコードはこれで動作します。その方法をお見せしましょう。さて、上記の例では、実際にはすべての名前空間を削除することはできません(2つの名前空間が使用されているため)。XMLドキュメントのどこかに、のようなものが必要になりますxmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo。例のクラスがより大きなドキュメントの一部である場合、名前空間の上のどこかで、とのどちらか(または両方)Abracadbraを宣言する必要がありWhoohooます。そうでない場合は、名前空間の一方または両方の要素を、何らかの接頭辞で装飾する必要があります(デフォルトの名前空間を2つ持つことはできませんよね?)。したがって、この例でAbracadabraは、デフォルトの名前空間です。MyTypeWithNamespacesクラス内に次のWhoohooように名前空間の名前空間プレフィックスを追加できます。

public MyTypeWithNamespaces
{
    this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
        new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
        new XmlQualifiedName("w", "urn:Whoohoo")
    });
}

これで、クラス定義で、<Label/>要素が名前空間"urn:Whoohoo"にあることを示したので、これ以上何もする必要はありません。上記のシリアル化コードを変更せずに使用してクラスをシリアル化すると、出力は次のようになります。

<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
    <w:Label>myLabel</w:Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

<Label>はドキュメントの他の部分とは異なる名前空間にあるため、何らかの方法で名前空間で「装飾」する必要があります。無残っていることに注意してくださいxsixsd名前空間。


これで、他の質問に対する私の回答は終了です。しかし、名前空間を使用しないことについてのOPの質問に確実に回答したかったのです。なぜなら、まだ対処していないように思うからです。それは想定し<Label>、この場合には、文書の残りの部分と同じ名前空間の一部ですurn:Abracadabra

<MyTypeWithNamespaces>
    <Label>myLabel<Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

コンストラクターは、最初のコード例と同じように見え、デフォルトの名前空間を取得するためのpublicプロパティも一緒に表示されます。

// 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[] {
        new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
    });
}

[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
    get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;

次に、後でMyTypeWithNamespacesオブジェクトを使用してシリアル化するコードで、上記のように呼び出します。

MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);

XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
    new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });

...

// Above, you'd setup your XmlTextWriter.

// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);

そして、XmlSerializer出力に追加の名前空間なしで、上記のように同じXMLを吐き出します。

<MyTypeWithNamespaces>
    <Label>myLabel<Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

完全を期すために、おそらくここで単にそれを参照するのではなく、正しい答えを含める必要があります。また、それが「サポートされていない動作」であるとどのように結論付けているのか知りたいです。
Dave Van den Eynde、2012年

1
これは私が見つけた最も簡単な説明なので、これを確認するために再びここに来ました。ありがとう@fourpastmidnight
Andre Albuquerque

2
わかりません。最終的なOPの答えとして、シリアル化中に「urn:Abracadabra」(コンストラクタ)で名前空間を使用していますが、なぜそれが最終出力に含まれていないのですか。OPは使用すべきではありません:XmlSerializerNamespaces EmptyXmlSerializerNamespaces = new XmlSerializerNamespaces(new [] {XmlQualifiedName.Empty});
dparkar 2013年

2
これは正解ですが、最も投票されたわけではありません。私にとってうまくいかなかった厄介なことは、私がXmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);に置き換える必要があったことvar xtw = XmlTextWriter.Create(memStm, xws);です。
Leonel Sanches da Silva 2016

1
私がこの回答を書いてから久しぶりです。XmlTextWriter.Create(abstract?)XmlWriterインスタンスを返します。したがって、@ Preza8は正しいので、他のXmlTextWriter固有のプロパティを(少なくとも、それをダウンキャストしないと)設定できず、したがって、への特定のキャストを失うことになりますXmlTextWriter
-fourpastmidnight

257
//Create our own namespaces for the output
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

//Add an empty namespace and empty value
ns.Add("", "");

//Create the serializer
XmlSerializer slz = new XmlSerializer(someType);

//Serialize the object with our own namespaces (notice the overload)
slz.Serialize(myXmlTextWriter, someObject, ns)

24
うーん...あなたたちは反逆者です。msdn.microsoft.com/en-us/library/で、それはできないと明示的に言っています。
ラルフラヴェル2012年

やったー!(msが言うことを克服するために、あなたは行うことができない)
granadaCoder 2013年

3
なぜ「サポートされていない」のかはわかりませんが、これはまさに私が望んでいたことです。
Dan Bechard、2014年

8
この回答は、「xmlns:d1p1」および「xmlns:q1」名前空間を生成します。これは何ですか?
Leonel Sanches da Silva

2
さて、このコードは、他の名前空間の定義なしで、本当に単純なシリアル化で機能します。複数の名前空間定義の場合、有効な答えは受け入れられたものです。
Leonel Sanches da Silva

6

代替手段があります- シリアル化する型にXmlSerializerNamespaces型のメンバーを提供できます。XmlNamespaceDeclarations属性で装飾します。名前空間プレフィックスとURIをそのメンバーに追加します。次に、XmlSerializerNamespacesを明示的に提供しないシリアル化では、型に入力した名前空間プレフィックス+ URIペアを使用します。

コード例、これがあなたのタイプだとしましょう:

[XmlRoot(Namespace = "urn:mycompany.2009")]
public class Person {
  [XmlAttribute] 
  public bool Known;
  [XmlElement]
  public string Name;
  [XmlNamespaceDeclarations]
  public XmlSerializerNamespaces xmlns;
}

あなたはこれを行うことができます:

var p = new Person
  { 
      Name = "Charley",
      Known = false, 
      xmlns = new XmlSerializerNamespaces()
  }
p.xmlns.Add("",""); // default namespace is emoty
p.xmlns.Add("c", "urn:mycompany.2009");

つまり、独自のプレフィックス+ URIペアのセットを指定しないインスタンスのシリアル化では、 "urn:mycompany.2009"名前空間に "p"プレフィックスが使用されます。また、xsiおよびxsd名前空間も省略されます。

ここでの違いは、XmlSerializer.Serialize()の呼び出しで明示的に使用するのではなく、タイプ自体にXmlSerializerNamespacesを追加することです。つまり、自分のタイプのインスタンスが、所有していないコード(webservicesスタックなど)によってシリアル化され、そのコードがXmlSerializerNamespacesを明示的に提供しない場合、そのシリアライザーはインスタンスで提供される名前空間を使用します。


1.違いがわかりません。XmlSerializerNamespacesのインスタンスにデフォルトの名前空間を追加しています。
Dave Van den Eynde、2009年

3
2.これにより、クラスがさらに汚染されます。私の目的は特定の名前空間を使用することではなく、名前空間をまったく使用しないことです。
Dave Van den Eynde、2009年

この方法と、シリアル化時にのみXmlSerializerNamespacesを指定する方法の違いについてのメモを追加しました。
Cheeso 2009年

0

私が使用しています:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        const string DEFAULT_NAMESPACE = "http://www.something.org/schema";
        var serializer = new XmlSerializer(typeof(Person), DEFAULT_NAMESPACE);
        var namespaces = new XmlSerializerNamespaces();
        namespaces.Add("", DEFAULT_NAMESPACE);

        using (var stream = new MemoryStream())
        {
            var someone = new Person
            {
                FirstName = "Donald",
                LastName = "Duck"
            };
            serializer.Serialize(stream, someone, namespaces);
            stream.Position = 0;
            using (var reader = new StreamReader(stream))
            {
                Console.WriteLine(reader.ReadToEnd());
            }
        }
    }
}

次のXMLを取得するには:

<?xml version="1.0"?>
<Person xmlns="http://www.something.org/schema">
  <FirstName>Donald</FirstName>
  <LastName>Duck</LastName>
</Person>

名前空間が不要な場合は、DEFAULT_NAMESPACEを ""に設定します。


この質問は10年以上前のものですが、当時の目的は、名前空間宣言がまったく含まれていないXML本文を持つことでした。
Dave Van den Eynde

1
私が10年前の質問に自分の回答を追加したのは、受け入れられた回答が完全版の聖書よりも読むのに長いためです。
マクセンス

そして、最も投票された回答は、推奨されないアプローチ(空の名前空間)を促進します。
Maxence

どうしようもない。私は、受け入れられた答えを、私が最も正しいと信じている答えにすることができます。
Dave Van den Eynde
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.