web-dev-qa-db-ja.com

複数のルートを持つC#XDocumentロード

ルートのないXMLファイルがあります。これは変更できません。解析しようとしていますが、XDocument.Loadでは解析できません。 ConformanceLevel.Fragmentを設定しようとしましたが、それでも例外がスローされます。誰かがこれに対する解決策を持っていますか?

XmlReaderで試してみましたが、物事が混乱し、正しく機能しません。 XDocument.Loadはうまく機能しますが、複数のルートを持つファイルがある場合は機能しません。

20
Darksody

XmlReader自体はxmlフラグメントの読み取りをサポートしています-つまり、.

_var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment };
using (var reader = XmlReader.Create("fragment.xml", settings))
{
  // you can work with reader just fine
}
_

ただし、_XDocument.Load_はフラグメント化されたxmlの読み取りをサポートしていません。

迅速で汚い方法は、_XDocument.Parse_を呼び出す前に、ノードを1つの仮想ルートの下にラップすることです。お気に入り:

_var fragments = File.ReadAllText("fragment.xml");
var myRootedXml = "<root>" + fragments + "</root>";
var doc = XDocument.Parse(myRootedXml);
_

このアプローチは小さなxmlファイルに限定されています-最初にファイルをメモリに読み込む必要があるためです。大きな文字列を連結するということは、メモリ内の大きなオブジェクトを移動することを意味します。これは避けるのが最善です。

パフォーマンスが重要な場合は、優れた@ Martin-Honnenの回答( https://stackoverflow.com)で説明されているように、ノードをXDocumentを介して1つずつXmlReaderに読み込む必要があります。/a/18203952/2440262

XmlReaderが有効なxmlを反復処理することを当然のことと見なし、パフォーマンスが重要であるAPIを使用する場合は、代わりに結合ストリームアプローチを使用できます。

_using (var jointStream = new MultiStream())
using (var openTagStream = new MemoryStream(Encoding.ASCII.GetBytes("<root>"), false))
using (var fileStream = 
  File.Open(@"fragment.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
using (var closeTagStream = new MemoryStream(Encoding.ASCII.GetBytes("</root>"), false))
{
    jointStream.AddStream(openTagStream);
    jointStream.AddStream(fileStream);
    jointStream.AddStream(closeTagStream);
    using (var reader = XmlReader.Create(jointStream))
    {
        // now you can work with reader as if it is reading valid xml
    }
}
_

MultiStream-たとえばを参照 https://Gist.github.com/svejdo1/b9165192d313ed0129a679c927379685

注:XDocumentは、xml全体をメモリにロードします。したがって、大きなファイルには使用しないでください。代わりに、反復にXmlReaderを使用し、XNode.ReadFrom(...)を介してクリスピービットのみをXElementとしてロードします。

18
Ondrej Svejdar

フラグメントを処理できる.NETFrameworkのメモリ内ツリー表現は.NETのDOM実装のXmlDocumentFragmentのみであるため、XmlDocumentとフラグメントを作成する必要があります。

XmlDocument doc = new XmlDocument();
XmlDocumentFragment frag = doc.CreateDocumentFragment();
frag.InnerXml = stringWithXml; // for instance 
                               // frag.InnerXml = File.ReadAllText("fragment.xml");

または、XPathDocumentで、ConformanceLevelをFragmentに設定してXmlReaderを使用して作成できます。

XPathDocument doc;
using (XmlReader xr = 
                 XmlReader.Create("fragment.xml", 
                                   new XmlReaderSettings()
                                   {
                                       ConformanceLevel = ConformanceLevel.Fragment
                                    }))
{
  doc = new XPathDocument(xr);
}

// new create XPathNavigator for read out data e.g.
XPathNavigator nav = doc.CreateNavigator();

明らかに、XPathNavigatorは読み取り専用です。

LINQ to XMLを使用する場合は、ラッパーとしてXElementを作成する必要があるという提案に同意します。ただし、ファイルの内容を含む文字列を取得する代わりに、XmlReaderでXNode.ReadFromを使用できます。

public static class MyExtensions
{
    public static IEnumerable<XNode> ParseFragment(XmlReader xr)
    {
        xr.MoveToContent();
        XNode node;
        while (!xr.EOF && (node = XNode.ReadFrom(xr)) != null)
        {
            yield return node;
        }
    }
}

その後

XElement root = new XElement("root", 
                             MyExtensions.ParseFragment(XmlReader.Create(
                                 "fragment.xml", 
                                 new XmlReaderSettings() {
                                 ConformanceLevel = ConformanceLevel.Fragment })));

これは、すべてを文字列に読み込むよりも効果的かつ効率的に機能する可能性があります。

13
Martin Honnen

XmlDocument.Load()を使用する場合は、コンテンツをルートノードでラップする必要があります。

またはあなたはこのような何かを試すことができます...

while (xmlReader.Read())
{
    if (xmlReader.NodeType == XmlNodeType.Element)
    {
        XmlDocument d = new XmlDocument();
        d.CreateElement().InnerText = xmlReader.ReadOuterXml();
     }
}
1
Secret Squirrel

XMLドキュメントに複数のルート要素を含めることはできません。 1つのルート要素が必要です。あなたは一つのことをするかもしれません。すべてのfragment要素を取得し、それらをルート要素にラップして、XDocumentで解析します。

これは、考えられる最善かつ最も簡単なアプローチです。

0
Vivek Jain