Próbuję jak najszybciej odczytać następujący dokument Xml i pozwolić dodatkowym klasom zarządzać odczytem każdego podbloku.
<ApplicationPool>
<Accounts>
<Account>
<NameOfKin></NameOfKin>
<StatementsAvailable>
<Statement></Statement>
</StatementsAvailable>
</Account>
</Accounts>
</ApplicationPool>
Jednak próbuję użyć obiektu XmlReader, aby odczytać każde konto, a następnie „StatementsAvailable”. Czy sugerujesz użycie XmlReader.Read i sprawdź każdy element i obsłuż go?
Pomyślałem o oddzieleniu moich klas, aby poprawnie obsługiwać każdy węzeł. Więc istnieje klasa AccountBase, która akceptuje wystąpienie XmlReader, które odczytuje NameOfKin i kilka innych właściwości dotyczących konta. Następnie chciałem przejść przez instrukcje i pozwolić innej klasie wypełnić informacje o instrukcji (a następnie dodać ją do IList).
Do tej pory wykonałem część „na klasę”, wykonując XmlReader.ReadElementString (), ale nie mogę poćwiczyć, jak nakazać wskaźnikowi przejście do elementu StatementsAvailable i pozwolić mi je iterować i pozwolić innej klasie odczytać każdą z tych cech .
Brzmi łatwo!
Odpowiedzi:
Z mojego doświadczenia
XmlReader
wynika, że bardzo łatwo jest przypadkowo przeczytać za dużo. Wiem, że powiedziałeś, że chcesz go przeczytać tak szybko, jak to możliwe, ale czy próbowałeś zamiast tego użyć modelu DOM? Odkryłem, że LINQ to XML znacznie ułatwia pracę z XML .Jeśli dokument jest szczególnie duży, możesz łączyć
XmlReader
i LINQ to XML, tworzącXElement
z anXmlReader
dla każdego z elementów „zewnętrznych” w sposób strumieniowy: pozwala to wykonać większość prac związanych z konwersją w LINQ to XML, ale nadal potrzebujesz tylko niewielka część dokumentu w pamięci w dowolnym momencie. Oto przykładowy kod (nieco dostosowany z tego wpisu na blogu ):Użyłem tego do konwersji danych użytkownika StackOverflow (które są ogromne) na inny format wcześniej - działa bardzo dobrze.
EDIT from radarbob, przeformatowany przez Jona - chociaż nie jest do końca jasne, do którego problemu „czytaj za daleko” jest mowa
Powinno to uprościć zagnieżdżanie i rozwiązać problem „odczyt za daleko”.
To rozwiązuje problem „odczyt zbyt daleko”, ponieważ implementuje klasyczny wzorzec pętli while:
źródło
if(reader.Name == elementName)
żebywhile(reader.Name == elementName)
naprawić problem wskazany przez PBZ?SimpleStreamAxis()
pomija elementy, gdy XML nie jest wcięty, ponieważNode.ReadFrom()
umieszcza czytnik w następnym węźle po załadowanym elemencie - który zostanie pominięty przez następny bezwarunkowyRead()
. Jeśli następny węzeł jest białą spacją, wszystko jest w porządku. W przeciwnym razie nie. Wersje bez tego problemu można znaleźć tutaj , tutaj lub tutaj .Trzy lata później, być może z ponownym naciskiem na dane WebApi i xml, natknąłem się na to pytanie. Ponieważ kodowo jestem skłonny podążać za Skeetem z samolotu bez spadochronu i widząc jego początkowy kod podwójnie potwierdzony przez artykuł zespołu MS Xml, a także przykład w BOL Streaming Transform of Large Xml Docs , bardzo szybko przeoczyłem inne komentarze , w szczególności od „pbz”, który wskazał, że jeśli masz te same elementy z nazwy po kolei, każdy inny jest pomijany z powodu podwójnego odczytu. W rzeczywistości artykuły na blogu BOL i MS analizowały dokumenty źródłowe z elementami docelowymi zagnieżdżonymi głębiej niż na drugim poziomie, maskując ten efekt uboczny.
Inne odpowiedzi dotyczą tego problemu. Chciałem tylko zaoferować nieco prostszą wersję, która wydaje się działać dobrze do tej pory i bierze pod uwagę, że xml może pochodzić z różnych źródeł, a nie tylko z URI, więc rozszerzenie działa na zarządzanym przez użytkownika XmlReader. Jedno założenie jest takie, że czytnik jest w stanie początkowym, ponieważ w przeciwnym razie pierwsza 'Read ()' może przejść przez żądany węzeł:
źródło
else Read()
obu. Dzięki, że to złapałeś.Cały czas wykonujemy ten rodzaj analizy XML. Kluczem jest określenie, gdzie metoda parsowania pozostawi czytnik przy wyjściu. Jeśli zawsze zostawisz czytnika na następnym elemencie następującym po elemencie, który został odczytany jako pierwszy, możesz bezpiecznie i przewidywalnie czytać w strumieniu XML. Jeśli więc czytnik aktualnie indeksuje
<Account>
element, po przeanalizowaniu czytelnik</Accounts>
zindeksuje tag zamykający.Kod parsowania wygląda mniej więcej tak:
Statements
Klasa tylko czyta w<StatementsAvailable>
węźleStatement
Klasa będzie wyglądać bardzo podobnieźródło
W przypadku podobiektów
ReadSubtree()
daje ci czytnik xml ograniczony do podobiektów, ale naprawdę myślę, że robisz to na własnej skórze. Jeśli nie masz bardzo szczegółowych wymagań dotyczących obsługi nietypowego / nieprzewidywalnego XML, użyjXmlSerializer
(być może w połączeniu z,sgen.exe
jeśli naprawdę chcesz).XmlReader
jest ... trudne. W przeciwieństwie do:źródło
Poniższy przykład przechodzi przez strumień, aby określić bieżący typ węzła, a następnie używa XmlWriter do wyprowadzenia zawartości XmlReader.
W poniższym przykładzie zastosowano metody XmlReader do odczytywania zawartości elementów i atrybutów.
źródło
Możesz zapętlić xmlnode i pobrać dane ...... C # czytnik XML
źródło
Nie mam doświadczenia, ale myślę, że XmlReader jest niepotrzebny. Jest bardzo trudny w użyciu.
XElement jest bardzo łatwy w użyciu.
Jeśli potrzebujesz wydajności (szybszej), musisz zmienić format pliku i użyć klas StreamReader i StreamWriter.
źródło