2009-08-04 11 views
1

Bonjour Fellow StackOverflowers,Comment analyser XML dans une chaîne dans .NET?

Je reçois une chaîne dans l'une de mes fonctions .NET. La chaîne vu de XML Visualiseur ressemble à ceci:

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

J'ai besoin pour analyser cette chaîne pour que je puisse écrire les données dans une table de données dont les colonnes sont les balises XML pour chaque donnée.

Dans le texte ci-dessus, j'aurais alors une datable qui aura 5 colonnes, nommées ID, CAT, DATE, REP_BY et LOCATION qui contiendront alors 4 lignes de données.

Dans la quatrième balise, notez que la balise n'a pas de données, mais qu'elle est marquée comme espace = "preserve". Cela signifierait que les données que je place dans mon datatable seraient vides pour la colonne LOCATION de la quatrième rangée.

Comment puis-je y parvenir? Des exemples de codes seraient très appréciés. Merci.

+0

En supposant que vous utilisez C#. Quelle version utilisez-vous? –

+0

.NET version 2.0 – Batuta

Répondre

4

Ceci est probablement la solution la plus simple pour obtenir le format XML sous forme de tableau. Lancer les attributs en utilisant des expressions régulières n'est pas très intelligent (et sûr), mais je n'aime pas l'API System.Xml et LINQ to XML n'est pas une option dans .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(); 
     } 
    } 
} 

MISE À JOUR

Ou se débarrasser de l'attribut en utilisant 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; 

Mais cela ne fonctionne pas parce que l'espace de noms XML sur le dernier élément LOCATION reste et le DataSet.LoadXml() se plaint qu'il y ait connot deux colonnes nommées LOCATION.

+0

Je ne sais pas pourquoi quelqu'un aurait downvote cette réponse. Utiliser un RegEx pour nettoyer la chaîne source? Même moi, qui déteste utiliser le traitement de chaînes sur XML avec une passion à toute épreuve, pourrait le faire dans ce cas. –

+0

J'ai voté cela comme la réponse, car cela m'a aidé à résoudre le cas que j'ai eu. – Batuta

1

N'utilisez pas l'analyse de chaîne. Essayez d'utiliser une bibliothèque xml (Linq a des objets qui pourraient vous aider). Vous ferez probablement cela beaucoup plus facilement.

+0

Toute autre méthode en dehors de LINQ? Je ne peux pas utiliser LINQ depuis que j'utilise .NET 2.0. Merci. – Batuta

+0

oh, d'accord. Alors Francis pourrait être plus utile dans votre cas. Mais je le laisse au cas où .NET 3.5 trouverait cette question;) –

8

Utilisation de la classe XmlReader. Cette classe est rapide et n'utilise pas beaucoup de mémoire mais lire le xml peut être difficile.

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

Pour obtenir un attribut du noeud courant que vous utilisez:

string value = reader.GetAttribute(name_of_attribute); 

Pour obtenir le texte intérieur d'un élément:

string innerText = reader.ReadString(); 

Utilisation de la classe XmlDocument. Cette classe est lente mais la manipulation et la lecture du xml est très facile car tout le xml est chargé.

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

Utilisation de la classe XDocument. L'avantage d'utiliser XDocument est que les éléments peuvent être accédés directement et simultanément. Cette classe utilise également la puissance de LINQ pour interroger le document XML.

using(StringReader tr = new StringReader(yourXMLString)) 
{ 
    XDocument doc = XDocument.Load(tr); 
    //do something 
} 
+0

Comment puis-je alors assembler les noms de colonnes du datatable en fonction des données de la chaîne? – Batuta

+0

Pourriez-vous s'il vous plaît fournir plus d'exemple de code en utilisant les données de chaîne réelles que j'ai posté? J'ai essayé votre code mais rien ne se présente dans ma variable xmlreader. Merci. – Batuta

+0

Mise à jour de l'exemple de code. –

2

Il existe des avantages et des inconvénients d'utiliser un sens plutôt qu'un autre.

Si vous cherchez un moyen de parcourir le XML en avant, en arrière et au hasard accéder à différents éléments, alors j'utiliser un XmlDocument.

(en supposant que votre XML pourrait être dans une chaîne)

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

Ensuite, vous pouvez alors utiliser quelque chose comme:

XmlNodeList xmlNodes = doc.SelectNodes(xPathString); 

Google sur XPath et vous trouverez quelques grands articles sur la façon de arriver à un certain élément dans votre document, puis vous pouvez le parcourir comme ceci:

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 
} 

Si votre fichier XML se trouve quelque part dans un fichier, vous pouvez le charger via doc.Load (filename) ou votre choix d'un objet stream.

Maintenant, si vous voulez seulement l'analyser une fois et que la chaîne XML est énorme, je suggère un StringReader/XmlReader car ils sont BEAUCOUP plus rapides car ils sont "forward only". Ce n'est peut-être pas la terminologie exacte, mais cela réduit l'utilisation de la mémoire en ne stockant pas tous les documents à la fois.

+0

Comme je l'ai mentionné, je voudrais seulement analyser la chaîne une fois et doit obtenir les données dans un datatable.Par coïncidence, les champs de colonne datatable doivent être les mêmes que ceux des données de chaîne (XML?). Merci. – Batuta

0

Je crois que vous pouvez simplement utiliser la méthode de la classe ADO.NET DataSetReadXml lire un document XML dans ce format, et il créerez les DataTable, DataColumn et DataRow objets pour vous. Vous devrez écrire une petite méthode de conversion si vous souhaitez par la suite transformer le type de données de la colonne DATE en DateTime. Mais à part ça, vous ne devriez pas avoir à fouiller avec XML du tout.

Modifier

Je vois de poste de Daniel Bruckner que les éléments de localisation dans l'espace de noms impair posent un problème. Eh bien, c'est assez facile à corriger:

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

Je ne suis pas un grand fan de xml moi-même, je dois l'utiliser comme source de données d'une grille pour le visualiser. Je reçois des sorties de notre serveur d'imagerie FileNet au format xml et j'ai besoin d'en extraire des morceaux pour remplir une base de données. Voici ce que je fais, 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 
Questions connexes