Câu hỏi Đọc Xml với XmlReader trong C #


Tôi đang cố đọc tài liệu Xml sau đây nhanh nhất có thể và để các lớp bổ sung quản lý việc đọc từng khối phụ.

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

Tuy nhiên, tôi đang cố gắng sử dụng đối tượng XmlReader để đọc từng Tài khoản và sau đó là "Bảng sao kê có sẵn". Bạn có đề nghị sử dụng XmlReader.Read và kiểm tra từng phần tử và xử lý nó?

Tôi đã nghĩ đến việc phân tách các lớp của tôi để xử lý từng nút một cách chính xác. Vì vậy, có một lớp AccountBase chấp nhận một cá thể XmlReader đọc NameOfKin và một số thuộc tính khác về tài khoản. Sau đó, tôi đã muốn giao tiếp thông qua các báo cáo và để cho một lớp khác tự điền vào Bản Tuyên Bố (và sau đó thêm nó vào một IList).

Vì vậy, đến nay tôi có phần "mỗi lớp" được thực hiện bằng cách làm XmlReader.ReadElementString () nhưng tôi không thể tập luyện làm thế nào để nói cho con trỏ để di chuyển đến phần tử StatementsAvailable và cho phép tôi lặp qua chúng và để cho một lớp khác đọc từng trong số các proeprties đó .

Nghe có vẻ dễ dàng!


76
2018-03-14 09:06


gốc


Nhấp vào dấu chấm hỏi màu cam ở góc trên bên phải của hộp chỉnh sửa để nhận trợ giúp chỉnh sửa. Có lẽ bạn muốn tạo một khối mã, được thực hiện bởi một dòng trống đầu tiên và sau đó mỗi dòng thụt vào với bốn dấu cách. - Anders Abel
hoặc chỉ cần chọn các dòng mã / XML của bạn và sau đó nhấp vào nút "mã" (101 010) trong thanh công cụ của trình soạn thảo - đơn giản như vậy! - marc_s


Các câu trả lời:


Kinh nghiệm của tôi về XmlReader là rất dễ dàng để vô tình đọc quá nhiều. Tôi biết bạn đã nói bạn muốn đọc nó càng nhanh càng tốt, nhưng có bạn đã thử sử dụng mô hình DOM thay thế? Tôi đã thấy rằng LINQ to XML làm cho XML hoạt động được nhiều nhiều dễ dàng hơn.

Nếu tài liệu của bạn đặc biệt lớn, bạn có thể kết hợp XmlReader và LINQ to XML bằng cách tạo XElement từ một XmlReader cho mỗi phần tử "bên ngoài" của bạn theo cách trực tuyến: điều này cho phép bạn thực hiện hầu hết công việc chuyển đổi trong LINQ thành XML, nhưng vẫn chỉ cần một phần nhỏ tài liệu trong bộ nhớ cùng một lúc. Dưới đây là một số mã mẫu (được điều chỉnh một chút từ bài đăng trên blog này):

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

Tôi đã sử dụng điều này để chuyển đổi dữ liệu người dùng StackOverflow (rất lớn) thành một định dạng khác trước đây - nó hoạt động rất tốt.

EDIT từ radarbob, định dạng lại bởi Jon - mặc dù nó không phải là khá rõ ràng mà "đọc quá xa" vấn đề đang được giới thiệu ...

Điều này sẽ đơn giản hóa việc lồng ghép và xử lý vấn đề "đọc quá xa".

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

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

    reader.ReadEndElement();
}

Thao tác này sẽ xử lý vấn đề "đọc quá xa" vì nó thực hiện mẫu vòng lặp cổ điển trong khi:

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

138
2018-03-14 09:17



Gọi hàm XNode.ReadFrom đọc phần tử và chuyển đến phần tử tiếp theo, sau đó đọc trình đọc sau. Đọc () đọc phần tiếp theo. Bạn về cơ bản sẽ bỏ lỡ một yếu tố nếu chúng xảy ra để có cùng tên và được liên tiếp. - pbz
@pbz: Cảm ơn. Tôi không chắc tôi tin tưởng bản thân mình để chỉnh sửa nó một cách chính xác (đó là bao nhiêu tôi không thích XmlReader :) Bạn có thể chỉnh sửa nó một cách chính xác? - Jon Skeet
@ Jon: :-) Tôi đã kết thúc làm một cái gì đó giống như Paul Alexander (dưới đây) được đề nghị. Sử dụng một nhãn (yuk) và làm cho nó nhảy đến lệnh đầu tiên trong vòng lặp "while", và do đó bỏ qua thêm "đọc" "nên" làm việc, tôi nghĩ rằng :-) - pbz
@ JonSkeet - Tôi có thể thiếu một cái gì đó nhưng sẽ không đơn giản thay đổi if(reader.Name == elementName) đến while(reader.Name == elementName) khắc phục vấn đề được chỉ ra bởi pbz? - David McLean
Như đã đề cập trong các nhận xét khác, phiên bản hiện tại của SimpleStreamAxis() sẽ bỏ qua các phần tử khi XML không được thụt vào, bởi vì Node.ReadFrom() định vị người đọc ở nút tiếp theo sau phần tử được tải - sẽ bị bỏ qua bởi điều kiện tiếp theo không điều kiện Read(). Nếu nút tiếp theo là khoảng trắng thì tất cả đều tốt. Nếu không, không. Đối với các phiên bản không có vấn đề này, hãy xem đây, đây hoặc là đây. - dbc


Ba năm sau, có lẽ với sự nhấn mạnh mới về dữ liệu WebApi và xml, tôi đã xem qua câu hỏi này. Kể từ khi codewise tôi nghiêng theo Skeet ra khỏi một chiếc máy bay mà không có một chiếc dù, và nhìn thấy mã ban đầu của ông gấp đôi corraborated bởi bài viết đội MS Xml cũng như một ví dụ trong BOL Chuyển đổi trực tuyến Tài liệu Xml Lớn, Tôi rất nhanh chóng bỏ qua các nhận xét khác, đặc biệt nhất từ ​​'pbz', người đã chỉ ra rằng nếu bạn có cùng các yếu tố theo tên liên tiếp, mọi người khác bị bỏ qua vì đọc đôi. Và trên thực tế, các bài viết trên blog BOL và MS đều phân tích cú pháp các tài liệu nguồn với các phần tử mục tiêu được lồng sâu hơn mức thứ hai, che dấu hiệu ứng phụ này.

Các câu trả lời khác giải quyết vấn đề này. Tôi chỉ muốn cung cấp một bản sửa đổi hơi đơn giản mà có vẻ hoạt động tốt cho đến nay, và đưa vào tài khoản rằng xml có thể đến từ các nguồn khác nhau, không chỉ là một uri, và do đó phần mở rộng hoạt động trên người dùng quản lý XmlReader. Giả định một là người đọc đang ở trạng thái ban đầu của nó, vì nếu không thì 'Read ()' đầu tiên có thể tiến qua một nút mong muốn:

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



Câu lệnh "if (reader.Name.Equals (elementName))" của bạn thiếu một trình đọc khác "tương ứng.Đọc ();" tuyên bố. Nếu phần tử không phải là những gì bạn muốn bạn muốn tiếp tục đọc. Đó là những gì tôi phải thêm vào để có được nó làm việc cho tôi. - Wes
@Wes Đã khắc phục sự cố bằng cách thu gọn hai điều kiện (NodeType và Name) để else Read() áp dụng cho cả hai. Cảm ơn vì đã bắt được điều đó. - mdisibio
Tôi upvoted bạn, nhưng tôi không phải là rất hạnh phúc để xem Đọc phương pháp gọi bằng văn bản hai lần. Có thể là bạn có thể sử dụng một vòng lặp while do? :) - nawfal
Một câu trả lời khác nhận thấy và giải quyết cùng một vấn đề với tài liệu MSDN: stackoverflow.com/a/18282052/3744182 - dbc


Chúng tôi thực hiện loại phân tích cú pháp XML này mọi lúc. Điều quan trọng là xác định nơi phương pháp phân tích cú pháp sẽ rời khỏi trình đọc khi thoát. Nếu bạn luôn để người đọc trên phần tử tiếp theo sau phần tử được đọc lần đầu thì bạn có thể đọc một cách an toàn và dự đoán được trong luồng XML. Vì vậy, nếu người đọc hiện đang lập chỉ mục <Account> phần tử, sau khi phân tích cú pháp người đọc sẽ lập chỉ mục </Accounts> đóng thẻ.

Mã phân tích trông giống như sau:

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

Các Statements lớp học chỉ đọc trong <StatementsAvailable> nút

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

Các Statement lớp học sẽ trông rất giống nhau

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





Đối với các đối tượng phụ, ReadSubtree() cung cấp cho bạn một trình đọc xml giới hạn đối với các đối tượng con, nhưng tôi có thật không nghĩ rằng bạn đang làm điều này một cách khó khăn. Trừ khi bạn có vô cùng đặc biệt các yêu cầu để xử lý xml không bình thường / không thể đạt được, sử dụng XmlSerializer (có lẽ kết hợp với sgen.exe nếu bạn thực sự muốn).

XmlReader là ... khó khăn. Tương phản với:

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





Ví dụ sau điều hướng qua luồng để xác định loại nút hiện tại và sau đó sử dụng XmlWriter để xuất nội dung 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();

Ví dụ sau sử dụng các phương thức XmlReader để đọc nội dung của các phần tử và các thuộc tính.

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





Tôi không có kinh nghiệm. Nhưng tôi nghĩ XmlReader là không cần thiết. Nó rất khó sử dụng.
XElement rất dễ sử dụng.
Nếu bạn cần hiệu suất (nhanh hơn), bạn phải thay đổi định dạng tệp và sử dụng các lớp StreamReader và 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");

Bạn có thể lặp qua xmlnode và lấy dữ liệu ...... C # XML Reader


-1
2018-04-03 05:52



Lớp này không được chấp nhận. Không được dùng. - nawfal