Как разобрать XML в строку в .NET? - PullRequest
1 голос
/ 04 августа 2009

Привет, товарищ по стеку,

Я получаю строку в одной из моих функций .NET. Строка при просмотре из XML Visualizer выглядит следующим образом:

- <root>
- <Table>
  <ID>ABC-123</ID>
  <CAT>Housekeeping</CAT>
  <DATE>21-JUN-2009</DATE>
  <REP_BY>John</REP_BY>
  <LOCATION>Head Office</LOCATION>
</Table>
- <Table>
  <ID>ABC-124</ID>
  <CAT>Environment</CAT>
  <DATE>23-JUN-2009</DATE>
  <REP_BY>Michelle</REP_BY>
  <LOCATION>Block C</LOCATION>
</Table>
- <Table>
  <ID>ABC-125</ID>
  <CAT>Staging</CAT>
  <DATE>21-JUN-2009</DATE>
  <REP_BY>George</REP_BY>
  <LOCATION>Head Office</LOCATION>
</Table>  
- <Table>
  <ID>ABC-123</ID>
  <CAT>Housekeeping</CAT>
  <DATE>21-JUN-2009</DATE>
  <REP_BY>John</REP_BY>
  <LOCATION space="preserve" xmlns="http://www.w3.org/XML/1998/namespace" /> 
</Table>  
</root>  

Мне нужно проанализировать эту строку, чтобы я мог записать данные в таблицу данных, столбцы которой представляют собой теги xml для всех данных.

В приведенном выше тексте у меня будет таблица данных с 5 столбцами с именами ID, CAT, DATE, REP_BY и LOCATION, которая будет содержать 4 строки данных.

В четвертом теге обратите внимание, что у него нет никаких данных, а скорее он помечен пробелом = "сохранить". Это будет означать, что данные, которые я помещаю в таблицу данных, будут пустыми для столбца LOCATION четвертой строки.

Как мне этого добиться? Примеры кодов будут высоко оценены. Спасибо.

Ответы [ 6 ]

8 голосов
/ 04 августа 2009

Использование класса XmlReader. Этот класс быстрый и не использует много памяти, но чтение XML может быть затруднено.

using (StringReader strReader = new StringReader(yourXMLString))
{
    using (XmlReader reader = XmlReader.Create(strReader))
    {
        while (reader.Read())
        {
            if(reader.Name == "Table" && reader.NodeType == reader.NodeType == XmlNodeType.Element)
            {
                using(XmlReader tableReader = reader.ReadSubtree())
                {
                    ReadTableNode(tableReader);
                }
            }
        }
    }
}

private void ReadTableNode(XmlReader reader)
{
    while (reader.Read())
    {
        if(reader.Name == "ID" && reader.NodeType == reader.NodeType == XmlNodeType.Element)
            //do something
        else if(reader.Name == "CAT" && reader.NodeType == reader.NodeType == XmlNodeType.Element)
            //do something

       //and continue....
    }
}

Для получения атрибута текущего узла вы используете:

string value = reader.GetAttribute(name_of_attribute);

Чтобы получить внутренний текст элемента:

string innerText =  reader.ReadString();

Использование класса XmlDocument. Этот класс медленный, но манипулировать и читать XML очень легко, потому что загружен весь XML.

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(yourXMLString);
//do something

Использование класса XDocument. Преимущество использования XDocument в том, что к элементам можно обращаться напрямую и одновременно. Этот класс также использует возможности LINQ для запроса XML-документа.

using(StringReader tr = new StringReader(yourXMLString))
{
    XDocument doc = XDocument.Load(tr);
    //do something
}
4 голосов
/ 04 августа 2009

Это, пожалуй, самое простое решение для перевода XML в табличную форму. Выбрасывать атрибуты с помощью регулярных выражений не так уж и умно (и безопасно), но мне не нравится API System.Xml, а LINQ to XML не поддерживается в .NET 2.0.

using System;
using System.Data;
using System.IO;
using System.Text.RegularExpressions;

namespace GeneralTestApplication
{
    class Program
    {
        private static void Main()
        {
            String input = @"<root><Table> [...] </root>";

            input = Regex.Replace(input, @" [a-zA-Z]+=""[^""]*""", String.Empty);

            DataSet dataSet = new DataSet();

            dataSet.ReadXml(new StringReader(input));

            foreach (DataRow row in dataSet.Tables[0].Rows)
            {
                foreach (DataColumn column in dataSet.Tables[0].Columns)
                {
                    Console.Write(row[column] + " | ");
                }
                Console.WriteLine();
            }

            Console.ReadLine();
        }
    }
}

UPDATE

Или избавиться от атрибута, используя System.Xml.

XmlDocument doc = new XmlDocument();

doc.Load(new StringReader(input));

foreach (XmlNode node in doc.SelectNodes("descendant-or-self::*"))
{
    node.Attributes.RemoveAll();
}

input = doc.OuterXml;

Но это не работает, поскольку пространство имен XML в последнем элементе LOCATION остается, а DataSet.LoadXml() жалуется, что не может быть двух столбцов с именем LOCATION.

2 голосов
/ 04 августа 2009

Есть преимущества и недостатки использования одного способа по сравнению с другим.

Если вы ищете способ обойти XML вперед, назад и получить произвольный доступ к различным элементам, я бы использовал XmlDocument.

(при условии, что ваш XML может быть в строке)

XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlVariable);

Тогда вы можете использовать что-то вроде:

XmlNodeList xmlNodes = doc.SelectNodes(xPathString);

Google о XPath, и вы найдете несколько замечательных статей о том, как добраться до определенного элемента в вашем документе, а затем вы можете просмотреть его следующим образом:

foreach(XmlNode node in xmlNodes)
{
   // do something with node.InnerText or any other property/method that you like
   // also through this method you can access the attributes of each node
   // allowing you to process that last LOCATION elements attributes
}

Если ваш xml-файл находится где-то в файле, вы можете загрузить его через doc.Load (имя файла) или по своему выбору объекта потока.

Теперь, если вы будете анализировать только один раз, а строка XML огромна, я предлагаю StringReader / XmlReader, так как они НАМНОГО быстрее, потому что они «только вперед». Возможно, это не точная терминология, но она сокращает использование памяти, поскольку не нужно хранить весь документ сразу.

1 голос
/ 04 августа 2009

Не используйте разбор строк. Попробуйте использовать некоторую библиотеку xml ( Linq содержит некоторые объекты, которые могут вам помочь). Вы, вероятно, будете делать это намного проще.

0 голосов
/ 04 августа 2009

Я сам не большой поклонник xml, мне нужно использовать его как источник данных сетки для его визуализации. Я получаю некоторые данные с нашего сервера обработки изображений FileNet в формате xml, и мне нужно извлечь их для заполнения базы данных. Вот что я делаю, HTH:

  Dim dsXML As DataSet
  Dim drXML As DataRow
  Dim rdr As System.IO.StringReader
  Dim docs() As String
  Dim SQL As String
  Dim xml As String
  Dim fnID As String

docs = _fnP8Dev.getDocumentsXML(_credToken, _docObjectStoreName, _docClass, "ReferenceNumber=" & fnID, "")
xml = docs(0)
If (InStr(xml, "<z:row") > 0) Then
 RaiseEvent msg("Inserting images for reference number " & fnID)
 rdr = New System.IO.StringReader(xml)
 dsXML = New DataSet
 dsXML.ReadXml(rdr)

 For Each drXML In dsXML.Tables(dsXML.Tables.Count - 1).Rows
   SQL = "Insert into fnImageP8 values ("
   SQL = SQL & "'" & drXML("Id") & "', "
   Try
    SQL = SQL & "'" & drXML("DocumentTitle") & "', "
   Catch ex As Exception
    SQL = SQL & "null, "
   End Try
0 голосов
/ 04 августа 2009

Я считаю, что вы можете просто использовать метод ReadXml класса ADO.NET DataSet для чтения XML-документа в этом формате, и он создаст объекты DataTable, DataColumn и DataRow для вы. Вам нужно будет написать небольшой метод преобразования, если вы хотите впоследствии изменить тип данных столбца DATE на DateTime. Но, кроме этого, вам вообще не нужно разбираться с XML.

Редактировать

Из поста Дэниела Брукнера я вижу, что элементы LOCATION в нечетном пространстве имен создают проблему. Ну, это достаточно легко исправить:

    XmlDocument d = new XmlDocument();
    d.LoadXml(xml);

    XmlNamespaceManager ns = new XmlNamespaceManager(d.NameTable);
    ns.AddNamespace("n", "http://www.w3.org/XML/1998/namespace");
    foreach (XmlNode n in d.SelectNodes("/root/Table/n:LOCATION", ns))
    {
        XmlElement loc = d.CreateElement("LOCATION");
        n.ParentNode.AppendChild(loc);
        n.ParentNode.RemoveChild(n);
    }

    DataSet ds = new DataSet();
    using (StringReader sr = new StringReader(d.OuterXml))
    {
        ds.ReadXml(sr);
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...