0

まず、私は .NET 2.0 に制約されているため、LINQ は私にとって選択肢ではありません (ただし、LINQ ソリューションがプロジェクトの .NET 3.5 への移行を推進するための餌食となるのが簡単な場合)。

ビルド時に xsd.exe を介して一連の C# クラスに変換される XSD があります。実行時に、XML ファイルがロードされ、C# クラスに逆シリアル化されます (この時点で検証が行われます)。次に、メモリ内の構成オブジェクト (XML ファイルのインポート中に入力された既定値を含む) を、キーと値のペアのディクショナリに変換する必要があります。

辞書のキーを値へのドット区切りのパスにしたいと思います。属性値と要素テキストは値と見なされ、他のすべてはそのキーになります。

例として、次の XML ファイルを想像してください。

<rootNode>
    <foo enabled="true"/>
    <bar enabled="false" myAttribute="5.6">
        <baz>Some Text</baz>
        <baz>Some other text.</baz>
    </bar>
</rootNode>

次のようなキーを持つ辞書になります。

"rootNode.foo.enabled" = (Boolean)true
"rootNode.bar.enabled" = (Boolean)false
"rootNode.bar.myAttribute" = (Float)5.6
"rootNode.bar.baz" = List<String> { "Some Text", "Some other text." }

注意すべきことは、rootNode が省略されているのは、それが特別だからではなく、テキストや属性がないためです。また、ディクショナリは、適切に型付けされたオブジェクトのディクショナリです (これは、デシリアライズで既に行われています。これが、XML を直接ではなく C# オブジェクトで作業したい理由の 1 つです)。

興味深いことに、xsd.exe によって作成されたオブジェクトは、既にが求めている形に非常に近いものになっています。クラス名は、myAttribute と呼ばれる float フィールドを持つ rootNodeFoo のようなものです。

私が検討したことの 1 つは、リフレクションを使用してオブジェクト ツリーを反復処理し、各オブジェクトのクラスの名前を使用してノードの名前を特定することです (ケーシングを微調整する必要がある場合があります)。少し)。これに関する問題は、それが間違った解決策のように感じられることです。これは、すべてのことをより高速に行うことができるはずのデシリアライザーに既にアクセスできるためです。

もう 1 つのオプションは、XSLT を使用して、データを希望の形式に直接シリアル化することです。ここでの問題は、私の XSLT の知識が限られていることです (間違っている場合は訂正してください) 途中で入力を失うと思います (すべてが文字列になります)。バックアウトします (今回は、.NET デシリアライザーを使用したときに得られる XSD 検証なし)。

重要な場合に備えて、XML ファイルから設定オブジェクトを取得するために使用している呼び出しは、次のようなものです。

var rootNode = new XmlRootAttribute();
rootNode.ElementName = "rootNode";
rootNode.Namespace = "urn:myNamespace";
var serializer = new XmlSerializer(typeof(rootNode), rootNode);
using (var reader = new StringReader(xmlString))
{
    var deserializedObject = (rootNode)serializer.Deserialize(reader);
}
4

2 に答える 2

2

最初の観察: オブジェクト グラフの使用は、ドット表現の生成を開始するのに最適な場所ではありません。名前があり、明確に定義された階層にあるノードについて話しているので、そこからある種のドット表記を生成したいと考えています。xml DOM はこれを行うのに最適な場所のようです。

問題の説明方法にはいくつかの問題があります。

1 つ目は、同じ名前の複数の要素を処理する場合の戦略です。この例では、辞書の値を実際にリストにすることで問題を回避しましたが、xml が次のようになっているとします。

<rootNode>
    <foo enabled="true">
        <bar enabled="false" myAttribute="5.6" />
        <bar enabled="true" myAttribute="3.4" />
    </foo>
</rootNode>

どちらがかなり明白であることに加えて、2 つのリーフfoo.enabled = (Boolean)trueに対してどのような辞書キーを提案しますか? myAttributeそれとも、単一のエントリがありfoo.bar.myAttribute = List<float> {5.6, 3.4}ますか? したがって、問題 1、類似した名前の複数の非リーフ ノードを処理する明確な方法はありません。

2 番目の問題は、リーフ ノード (つまり、属性または要素の値) で最終的な変換を行うためのデータ型の選択にあります。に書き込んでいる場合はDictionary<string, object>、読み取られる要素/属性のスキーマ単純型に基づいて型を選択することをお勧めします。その方法はわかりませんが、System.Convertクラスのさまざまな用途を調べることをお勧めします。

差し当たり、問題 #1 が表面化することはなく、Dictionary<string, string>実装に問題がないと仮定すると、開始するためのコードがいくつかあります。

static void Main(string[] args)
{
    var xml = @"
<rootNode>
    <foo enabled=""true"">
         <bar enabled=""false"" myAttribute=""5.6"" />
         <baz>Text!</baz>
    </foo>
</rootNode>
";

    var document = new XmlDocument();
    document.LoadXml(xml);
    var retVal = new Dictionary<string, string>();
    Go(retVal, document.DocumentElement, new List<string>());
}

private static void Go(Dictionary<string, string> theDict, XmlElement start, List<string> keyTokens)
{
    // Process simple content
    var textNode = start.ChildNodes.OfType<XmlText>().SingleOrDefault();
    if (textNode != null)
    {
        theDict[string.Join(".", keyTokens.ToArray())] = textNode.Value;
    }

    // Process attributes
    foreach (XmlAttribute att in start.Attributes)
    {
        theDict[string.Join(".", keyTokens.ToArray()) + "." + att.Name] = att.Value;
    }

    // Process child nodes
    foreach (var childNode in start.ChildNodes.OfType<XmlElement>())
    {
        Go(theDict, childNode, new List<string>(keyTokens) { childNode.Name });   // shorthand for .Add
    }
}

結果は次のとおりです。

サンプルコードの実行結果

于 2013-10-01T23:05:53.523 に答える