2

次のXML構造を生成することを検討してください。この構造には、2つの接頭辞付き名前空間があります。

XNamespace ns1 = "http://www.namespace.org/ns1";
const string prefix1 = "w1";
XNamespace ns2 = "http://www.namespace.org/ns2";
const string prefix2 = "w2";

var root = 
    new XElement(ns1 + "root", 
        new XElement(ns1 + "E1"
            , new XAttribute(ns1 + "attr1", "value1")
            , new XAttribute(ns2 + "attr2", "value2"))
        , new XAttribute(XNamespace.Xmlns + prefix2, ns2)
        , new XAttribute(XNamespace.Xmlns + prefix1, ns1)
    );

次のXML結果を生成します(これは問題ありません)。

<w1:root xmlns:w2="http://www.namespace.org/ns2" xmlns:w1="http://www.namespace.org/ns1">
  <w1:E1 w1:attr1="value1" w2:attr2="value2" />
</w1:root>

ns1次のように、XML宣言をコメントアウトして、プレフィックス付きの名前空間からデフォルトの名前空間に変更しようとすると、問題が発生します。

var root = 
    new XElement(ns1 + "root", 
        new XElement(ns1 + "E1"
            , new XAttribute(ns1 + "attr1", "value1")
            , new XAttribute(ns2 + "attr2", "value2"))
        , new XAttribute(XNamespace.Xmlns + prefix2, ns2)
        //, new XAttribute(XNamespace.Xmlns + prefix1, ns1)
    );

これは以下を生成します:

<root xmlns:w2="http://www.namespace.org/ns2" xmlns="http://www.namespace.org/ns1">
  <E1 p3:attr1="value1" w2:attr2="value2" xmlns:p3="http://www.namespace.org/ns1" />
</root>

rootE1属性の重複するp3名前空間定義に注意してくださいE1。これを回避するにはどうすればよいですか?ルート要素でデフォルトの名前空間の宣言を強制するにはどうすればよいですか?

関連する質問

私はこの質問を研究しました:XDocumentのデフォルトのXML名前空間を設定する方法

しかし、提案された答えは、名前空間が定義されていない要素の名前空間を置き換えます。私のサンプルでは、​​要素と属性にはすでに名前空間が正しく設定されています。

私が試したこと

試行錯誤が多すぎるため、ルートノードの直下にない属性は、属性とその直接の親要素の両方がデフォルトの名前空間と同じ名前空間を持っているように見えます。属性の名前空間を削除する必要があります!!!

これに基づいて、結果のXMLのすべての要素をトラバースし、上記を実行する次の拡張メソッドを定義しました。これまでのすべてのサンプルで、この拡張メソッドは問題を正常に修正しましたが、必ずしも誰かが失敗した例を作成できないことを意味するわけではありません。

public static void FixDefaultXmlNamespace(this XElement xelem, XNamespace ns)
{
    if(xelem.Parent != null && xelem.Name.Namespace == ns)
    {
        if(xelem.Attributes().Any(x => x.Name.Namespace == ns))
        {
            var attrs = xelem.Attributes().ToArray();
            for (int i = 0; i < attrs.Length; i++)
            {
                var attr = attrs[i];
                if (attr.Name.Namespace == ns)
                {
                    attrs[i] = new XAttribute(attr.Name.LocalName, attr.Value);
                }
            }

            xelem.ReplaceAttributes(attrs);
        }
    }

    foreach (var elem in xelem.Elements())
        elem.FixDefaultXmlNamespace(ns);
}

この拡張メソッドは、私たちの質問に対して次のXMLを生成します。これは私が望むものです。

<root xmlns:w2="http://www.namespace.org/ns2" xmlns="http://www.namespace.org/ns1">
  <E1 attr1="value1" w2:attr2="value2" />
</root>

しかし、主に高価であるため、このソリューションは好きではありません。どこかに小さな設定が欠けているような気がします。何か案は?

4

2 に答える 2

1

ここから引用:

属性は、その親要素の子とは見なされません。属性がその親要素の名前空間を継承することはありません。そのため、属性には適切な名前空間プレフィックスがある場合にのみ名前空間に含まれます。属性をデフォルトの名前空間にすることはできません。

そしてここに

デフォルトの名前空間宣言は、スコープ内の接頭辞のないすべての要素名に適用されます。デフォルトの名前空間宣言は、属性名に直接適用されません。接頭辞のない属性の解釈は、それらが表示される要素によって決定されます。

LINQ-to-XMLのこの奇妙な動作は、標準に根ざしているようです。したがって、新しい属性を追加するときは常に、その名前空間を、そのスコープでアクティブな親のデフォルトの名前空間と比較する必要があります。属性を追加するためにこの拡張メソッドを使用します。

public static XAttribute AddAttributeNamespaceSafe(this XElement parent, 
         XName attrName, string attrValue, XNamespace documentDefaultNamespace)
{
    if (newAttrName.Namespace == documentDefaultNamespace)
        attrName = attrName.LocalName;

    var newAttr = new XAttribute(attrName, attrValue);
    parent.Add(newAttr);
    return newAttr;
}

そして、属性を取得するためにこの拡張メソッドを使用します。

public static XAttribute GetAttributeNamespaceSafe(this XElement parent, 
        XName attrName, XNamespace documentDefaultNamespace)
{
    if (attrName.Namespace == documentDefaultNamespace)
        attrName = attrName.LocalName;
    return parent.Attribute(attrName);
}

または、XML構造が手元にあり、属性にすでに追加されている名前空間を修正する場合は、次の拡張メソッドを使用してこれを修正します(これは質問で概説されているものとは少し異なります)。

public static void FixDefaultXmlNamespace(this XElement xelem, 
        XNamespace documentDefaultNamespace)
{
    if (xelem.Attributes().Any(x => x.Name.Namespace == documentDefaultNamespace))
    {
        var attrs = xelem.Attributes().ToArray();
        for (int i = 0; i < attrs.Length; i++)
        {
            var attr = attrs[i];
            if (attr.Name.Namespace == documentDefaultNamespace)
            {
                attrs[i] = new XAttribute(attr.Name.LocalName, attr.Value);
            }
        }

        xelem.ReplaceAttributes(attrs);
    }

    foreach (var elem in xelem.Elements())
        elem.FixDefaultXmlNamespace(documentDefaultNamespace);
}

属性の追加および取得時に最初の2つの方法を使用した場合は、上記の方法を適用する必要がないことに注意してください。

于 2013-02-25T11:00:41.197 に答える
0

一言で言えば本のC#からあなたのために何かを見つけました:

属性に名前空間を割り当てることもできます。主な違いは、常にプレフィックスが必要なことです。例えば:

<customer xmlns:nut="OReilly.Nutshell.CSharp" nut:id="123" />

もう1つの違いは、修飾されていない属性には常に空の名前空間があることです。親要素からデフォルトの名前空間を継承することはありません。

だからあなたの望む出力を与えられて、私は簡単なチェックをしました。

        var xml = @"<root xmlns:w2=""http://www.namespace.org/ns2"" xmlns=""http://www.namespace.org/ns1"">
                <E1 attr1=""value1"" w2:attr2=""value2"" />
            </root>";

        var dom = XElement.Parse(xml);
        var e1 = dom.Element(ns1 + "E1");

        var attr2 = e1.Attribute(ns2 + "attr2");
        var attr1 = e1.Attribute(ns1 + "attr1");
        // attr1 is null !

        var attrNoNS = e1.Attribute("attr1");
        // attrNoNS is not null

つまり、attr1にはデフォルトの名前空間はありませんが、空の名前空間があります。

これはあなたが欲しいですか?はいの場合は、名前空間なしでattr1を作成します...

new XAttribute("attr1", "value1")
于 2013-02-20T07:28:41.433 に答える