Pertanyaan Membaca Xml dengan XmlReader di C #


Saya mencoba membaca dokumen Xml berikut secepat yang saya bisa dan membiarkan kelas tambahan mengelola pembacaan setiap sub blok.

<ApplicationPool>
    <Accounts>
        <Account>
            <NameOfKin></NameOfKin>
            <StatementsAvailable>
                <Statement></Statement>
            </StatementsAvailable>
        </Account>
    </Accounts>
</ApplicationPool>

Namun, saya mencoba menggunakan objek XmlReader untuk membaca setiap Akun dan kemudian "StatementAvailable". Apakah Anda menyarankan menggunakan XmlReader.Baca dan periksa setiap elemen dan tangani?

Saya sudah berpikir untuk memisahkan kelas saya untuk menangani setiap node dengan benar. Jadi ada kelas AccountBase yang menerima contoh XmlReader yang membaca NameOfKin dan beberapa properti lain tentang akun tersebut. Kemudian saya ingin melakukan interasi melalui Pernyataan dan membiarkan kelas lain mengisi sendiri tentang Pernyataan (dan kemudian menambahkannya ke IList).

Sejauh ini saya memiliki bagian "per kelas" yang dilakukan dengan melakukan XmlReader.ReadElementString () tetapi saya tidak dapat melatih bagaimana memberi tahu pointer untuk berpindah ke StatementAvailable elemen dan biarkan saya beralih melalui mereka dan biarkan kelas lain membaca masing-masing proeprties .

Kedengarannya mudah!


76
2018-03-14 09:06


asal


Jawaban:


Pengalaman saya tentang XmlReader adalah sangat mudah untuk tidak sengaja membaca terlalu banyak. Saya tahu Anda telah mengatakan bahwa Anda ingin membacanya secepat mungkin, tetapi memiliki Anda mencoba menggunakan model DOM saja? Saya telah menemukan bahwa LINQ to XML membuat XML bekerja jauh banyak lebih mudah.

Jika dokumen Anda sangat besar, Anda dapat menggabungkan XmlReader dan LINQ ke XML dengan membuat XElement dari sebuah XmlReader untuk setiap elemen "luar" Anda secara streaming: ini memungkinkan Anda melakukan sebagian besar pekerjaan konversi di LINQ ke XML, tetapi masih hanya membutuhkan sebagian kecil dokumen dalam memori pada satu waktu. Berikut ini beberapa contoh kode (sedikit diadaptasi dari posting blog ini):

static IEnumerable<XElement> SimpleStreamAxis(string inputUrl,
                                              string elementName)
{
  using (XmlReader reader = XmlReader.Create(inputUrl))
  {
    reader.MoveToContent();
    while (reader.Read())
    {
      if (reader.NodeType == XmlNodeType.Element)
      {
        if (reader.Name == elementName)
        {
          XElement el = XNode.ReadFrom(reader) as XElement;
          if (el != null)
          {
            yield return el;
          }
        }
      }
    }
  }
}

Saya telah menggunakan ini untuk mengonversi data pengguna StackOverflow (yang sangat besar) ke dalam format lain sebelumnya - ini bekerja dengan sangat baik.

EDIT dari radarbob, diformat ulang oleh Jon - meskipun tidak cukup jelas yang "membaca terlalu jauh" masalah sedang dirujuk ke ...

Ini harus menyederhanakan peneluran dan mengurus masalah "membaca terlalu jauh".

using (XmlReader reader = XmlReader.Create(inputUrl))
{
    reader.ReadStartElement("theRootElement");

    while (reader.Name == "TheNodeIWant")
    {
        XElement el = (XElement) XNode.ReadFrom(reader);
    }

    reader.ReadEndElement();
}

Ini menangani masalah "membaca terlalu jauh" karena mengimplementasikan pola lingkaran sementara klasik:

initial read;
(while "we're not at the end") {
    do stuff;
    read;
}

138
2018-03-14 09:17



Tiga tahun kemudian, mungkin dengan penekanan baru pada data WebApi dan xml, saya menemukan pertanyaan ini. Sejak codewise saya cenderung untuk mengikuti Skeet keluar dari pesawat tanpa parasut, dan melihat kode awalnya dua kali dikorelasikan oleh artikel tim MS Xml serta contoh di BOL Streaming Transformasi Dokumen Xml BesarSaya dengan cepat mengabaikan komentar-komentar lainnya, terutama dari 'pbz', yang menunjukkan bahwa jika Anda memiliki unsur-unsur yang sama dengan nama berturut-turut, setiap orang lainnya dilewati karena membaca ganda. Dan pada kenyataannya, artikel blog BOL dan MS keduanya mengurai dokumen sumber dengan elemen target bersarang lebih dalam dari tingkat kedua, menutupi efek samping ini.

Jawaban lain mengatasi masalah ini. Saya hanya ingin menawarkan revisi yang sedikit lebih sederhana yang tampaknya berfungsi dengan baik sejauh ini, dan memperhitungkan bahwa xml mungkin berasal dari sumber yang berbeda, bukan hanya uri, sehingga ekstensi bekerja pada XmlReader yang dikelola pengguna. Asumsi yang pertama adalah bahwa pembaca berada dalam keadaan awal, karena jika tidak, yang pertama, 'Baca ()' dapat maju melewati simpul yang diinginkan:

public static IEnumerable<XElement> ElementsNamed(this XmlReader reader, string elementName)
{
    reader.MoveToContent(); // will not advance reader if already on a content node; if successful, ReadState is Interactive
    reader.Read();          // this is needed, even with MoveToContent and ReadState.Interactive
    while(!reader.EOF && reader.ReadState == ReadState.Interactive)
    {
        // corrected for bug noted by Wes below...
        if(reader.NodeType == XmlNodeType.Element && reader.Name.Equals(elementName))
        {
             // this advances the reader...so it's either XNode.ReadFrom() or reader.Read(), but not both
             var matchedElement = XNode.ReadFrom(reader) as XElement;
             if(matchedElement != null)
                 yield return matchedElement;
        }
        else
            reader.Read();
    }
}

25
2017-10-03 17:38



Kami melakukan parsing XML semacam ini sepanjang waktu. Kuncinya adalah menentukan di mana metode parsing akan meninggalkan pembaca saat keluar. Jika Anda selalu meninggalkan pembaca pada elemen berikutnya mengikuti elemen yang pertama kali dibaca maka Anda dapat membaca dengan aman dan dapat diduga dalam aliran XML. Jadi jika pembaca saat ini mengindeks <Account> elemen, setelah parsing pembaca akan mengindeks </Accounts> tag penutup.

Kode parsing terlihat seperti ini:

public class Account
{
    string _accountId;
    string _nameOfKin;
    Statements _statmentsAvailable;

    public void ReadFromXml( XmlReader reader )
    {
        reader.MoveToContent();

        // Read node attributes
        _accountId = reader.GetAttribute( "accountId" );
        ...

        if( reader.IsEmptyElement ) { reader.Read(); return; }

        reader.Read();
        while( ! reader.EOF )
        {
            if( reader.IsStartElement() )
            {
                switch( reader.Name )
                {
                    // Read element for a property of this class
                    case "NameOfKin":
                        _nameOfKin = reader.ReadElementContentAsString();
                        break;

                    // Starting sub-list
                case "StatementsAvailable":
                    _statementsAvailable = new Statements();
                    _statementsAvailable.Read( reader );
                    break;

                    default:
                        reader.Skip();
                }
            }
            else
            {
                reader.Read();
                break;
            }
        }       
    }
}

Itu Statements kelas hanya membaca di <StatementsAvailable> simpul

public class Statements
{
    List<Statement> _statements = new List<Statement>();

    public void ReadFromXml( XmlReader reader )
    {
        reader.MoveToContent();
        if( reader.IsEmptyElement ) { reader.Read(); return; }

        reader.Read();
        while( ! reader.EOF )
        {
            if( reader.IsStartElement() )
            {
                if( reader.Name == "Statement" )
                {
                    var statement = new Statement();
                    statement.ReadFromXml( reader );
                    _statements.Add( statement );               
                }
                else
                {
                    reader.Skip();
                }
            }
            else
            {
                reader.Read();
                break;
            }
        }
    }
}

Itu Statement kelas akan terlihat sangat mirip

public class Statement
{
    string _satementId;

    public void ReadFromXml( XmlReader reader )
    {
        reader.MoveToContent();

        // Read noe attributes
        _statementId = reader.GetAttribute( "statementId" );
        ...

        if( reader.IsEmptyElement ) { reader.Read(); return; }

        reader.Read();
        while( ! reader.EOF )
        {           
            ....same basic loop
        }       
    }
}

15
2018-03-14 09:41



Untuk sub-objek, ReadSubtree() memberi Anda pembaca xml terbatas pada sub-objek, tetapi saya sangat berpikir bahwa Anda melakukan ini dengan cara yang sulit. Kecuali kamu punya sangat spesifik persyaratan untuk menangani xml yang tidak biasa / tidak dapat diprediksi, gunakan XmlSerializer (mungkin digabungkan dengan sgen.exe jika Anda benar-benar menginginkannya).

XmlReader itu ... rumit. Kontras ke:

using System;
using System.Collections.Generic;
using System.Xml.Serialization;
public class ApplicationPool {
    private readonly List<Account> accounts = new List<Account>();
    public List<Account> Accounts {get{return accounts;}}
}
public class Account {
    public string NameOfKin {get;set;}
    private readonly List<Statement> statements = new List<Statement>();
    public List<Statement> StatementsAvailable {get{return statements;}}
}
public class Statement {}
static class Program {
    static void Main() {
        XmlSerializer ser = new XmlSerializer(typeof(ApplicationPool));
        ser.Serialize(Console.Out, new ApplicationPool {
            Accounts = { new Account { NameOfKin = "Fred",
                StatementsAvailable = { new Statement {}, new Statement {}}}}
        });
    }
}

5
2018-03-14 09:15



Contoh berikut menavigasi melalui aliran untuk menentukan jenis node saat ini, dan kemudian menggunakan XmlWriter untuk menampilkan konten XmlReader.

    StringBuilder output = new StringBuilder();

    String xmlString =
            @"<?xml version='1.0'?>
            <!-- This is a sample XML document -->
            <Items>
              <Item>test with a child element <more/> stuff</Item>
            </Items>";
    // Create an XmlReader
    using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
    {
        XmlWriterSettings ws = new XmlWriterSettings();
        ws.Indent = true;
        using (XmlWriter writer = XmlWriter.Create(output, ws))
        {

            // Parse the file and display each of the nodes.
            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        writer.WriteStartElement(reader.Name);
                        break;
                    case XmlNodeType.Text:
                        writer.WriteString(reader.Value);
                        break;
                    case XmlNodeType.XmlDeclaration:
                    case XmlNodeType.ProcessingInstruction:
                        writer.WriteProcessingInstruction(reader.Name, reader.Value);
                        break;
                    case XmlNodeType.Comment:
                        writer.WriteComment(reader.Value);
                        break;
                    case XmlNodeType.EndElement:
                        writer.WriteFullEndElement();
                        break;
                }
            }

        }
    }
    OutputTextBlock.Text = output.ToString();

Contoh berikut menggunakan metode XmlReader untuk membaca konten elemen dan atribut.

StringBuilder output = new StringBuilder();

String xmlString =
    @"<bookstore>
        <book genre='autobiography' publicationdate='1981-03-22' ISBN='1-861003-11-0'>
            <title>The Autobiography of Benjamin Franklin</title>
            <author>
                <first-name>Benjamin</first-name>
                <last-name>Franklin</last-name>
            </author>
            <price>8.99</price>
        </book>
    </bookstore>";

// Create an XmlReader
using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
{
    reader.ReadToFollowing("book");
    reader.MoveToFirstAttribute();
    string genre = reader.Value;
    output.AppendLine("The genre value: " + genre);

    reader.ReadToFollowing("title");
    output.AppendLine("Content of the title element: " + reader.ReadElementContentAsString());
}

OutputTextBlock.Text = output.ToString();

1
2018-04-14 07:49



Saya tidak berpengalaman. Tapi saya pikir XmlReader tidak diperlukan. Ini sangat sulit digunakan.
XElement sangat mudah digunakan.
Jika Anda membutuhkan kinerja (lebih cepat) Anda harus mengubah format file dan menggunakan kelas StreamReader dan StreamWriter.


0
2017-12-28 09:08



    XmlDataDocument xmldoc = new XmlDataDocument();
    XmlNodeList xmlnode ;
    int i = 0;
    string str = null;
    FileStream fs = new FileStream("product.xml", FileMode.Open, FileAccess.Read);
    xmldoc.Load(fs);
    xmlnode = xmldoc.GetElementsByTagName("Product");

Anda dapat mengulang melalui xmlnode dan mendapatkan data ...... C # XML Reader


-1
2018-04-03 05:52