LINQ pour lire XML


194

J'ai obtenu ce fichier XML:

<root>
    <level1 name="A">
        <level2 name="A1" />
        <level2 name="A2" />
    </level1>
    <level1 name="B">
        <level2 name="B1" />
        <level2 name="B2" />
    </level1>
    <level1 name="C" />
</root>

Quelqu'un pourrait-il me donner un code C # en utilisant LINQ, la façon la plus simple d'imprimer ce résultat:
(Notez l'espace supplémentaire s'il s'agit d'un level2nœud)

A
  A1
  A2
B
  B1
  B2
C

Actuellement, j'ai ce code:

XDocument xdoc = XDocument.Load("data.xml"));
var lv1s = from lv1 in xdoc.Descendants("level1")
           select lv1.Attribute("name").Value;

foreach (var lv1 in lv1s)
{
    result.AppendLine(lv1);

    var lv2s = from lv2 in xdoc...???
}

14
Voici un bel exemple de ce dont vous avez

Réponses:


224

Essaye ça.

using System.Xml.Linq;

void Main()
{
    StringBuilder result = new StringBuilder();

    //Load xml
    XDocument xdoc = XDocument.Load("data.xml");

    //Run query
    var lv1s = from lv1 in xdoc.Descendants("level1")
               select new { 
                   Header = lv1.Attribute("name").Value,
                   Children = lv1.Descendants("level2")
               };

    //Loop through results
    foreach (var lv1 in lv1s){
            result.AppendLine(lv1.Header);
            foreach(var lv2 in lv1.Children)
                 result.AppendLine("     " + lv2.Attribute("name").Value);
    }

    Console.WriteLine(result);
}

3
@bendewey Je pose une question similaire, pourriez-vous la vérifier ici: stackoverflow.com/questions/13247449/…
Saeid

2
C'est comme prendre un porte-avions juste pour aller pêcher.
TomeeNS

53

Ou, si vous voulez une approche plus générale - c'est-à-dire pour l'imbrication jusqu'au "niveau N":

void Main()
{
    XElement rootElement = XElement.Load(@"c:\events\test.xml");

    Console.WriteLine(GetOutline(0, rootElement));  
}

private string GetOutline(int indentLevel, XElement element)
{
    StringBuilder result = new StringBuilder();

    if (element.Attribute("name") != null)
    {
        result = result.AppendLine(new string(' ', indentLevel * 2) + element.Attribute("name").Value);
    }

    foreach (XElement childElement in element.Elements())
    {
        result.Append(GetOutline(indentLevel + 1, childElement));
    }

    return result.ToString();
}

23

Un couple de vieilles foreachboucles simples fournit une solution propre:

foreach (XElement level1Element in XElement.Load("data.xml").Elements("level1"))
{
    result.AppendLine(level1Element.Attribute("name").Value);

    foreach (XElement level2Element in level1Element.Elements("level2"))
    {
        result.AppendLine("  " + level2Element.Attribute("name").Value);
    }
}

19

Voici quelques exemples de travail complets qui s'appuient sur les exemples @bendewey & @dommer. J'avais besoin de modifier un peu chacun pour le faire fonctionner, mais au cas où un autre noob LINQ chercherait des exemples de travail, voici:

//bendewey's example using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

class loadXMLToLINQ1
{
    static void Main( )
    {
        //Load xml
        XDocument xdoc = XDocument.Load(@"c:\\data.xml"); //you'll have to edit your path

        //Run query
        var lv1s = from lv1 in xdoc.Descendants("level1")
           select new 
           { 
               Header = lv1.Attribute("name").Value,
               Children = lv1.Descendants("level2")
            };

        StringBuilder result = new StringBuilder(); //had to add this to make the result work
        //Loop through results
        foreach (var lv1 in lv1s)
        {
            result.AppendLine("  " + lv1.Header);
            foreach(var lv2 in lv1.Children)
            result.AppendLine("    " + lv2.Attribute("name").Value);
        }
        Console.WriteLine(result.ToString()); //added this so you could see the output on the console
    }
}

Et ensuite:

//Dommer's example, using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

class loadXMLToLINQ
{
static void Main( )
    {
        XElement rootElement = XElement.Load(@"c:\\data.xml"); //you'll have to edit your path
        Console.WriteLine(GetOutline(0, rootElement));  
    }

static private string GetOutline(int indentLevel, XElement element)
    {
        StringBuilder result = new StringBuilder();
        if (element.Attribute("name") != null)
        {
            result = result.AppendLine(new string(' ', indentLevel * 2) + element.Attribute("name").Value);
        }
        foreach (XElement childElement in element.Elements())
        {
            result.Append(GetOutline(indentLevel + 1, childElement));
        }
        return result.ToString();
    }
}

Ces deux compilent et fonctionnent dans VS2010 en utilisant la version 4.0.30319.1 de csc.exe et donnent exactement la même sortie. J'espère que cela aidera quelqu'un d'autre qui cherche des exemples de code de travail.

EDIT: ajout de l'exemple de @eglasius également car il m'est devenu utile:

//@eglasius example, still using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

class loadXMLToLINQ2
{
    static void Main( )
    {
        StringBuilder result = new StringBuilder(); //needed for result below
        XDocument xdoc = XDocument.Load(@"c:\\deg\\data.xml"); //you'll have to edit your path
        var lv1s = xdoc.Root.Descendants("level1"); 
        var lvs = lv1s.SelectMany(l=>
             new string[]{ l.Attribute("name").Value }
             .Union(
                 l.Descendants("level2")
                 .Select(l2=>"   " + l2.Attribute("name").Value)
              )
            );
        foreach (var lv in lvs)
        {
           result.AppendLine(lv);
        }
        Console.WriteLine(result);//added this so you could see the result
    }
}

8
XDocument xdoc = XDocument.Load("data.xml");
var lv1s = xdoc.Root.Descendants("level1"); 
var lvs = lv1s.SelectMany(l=>
     new string[]{ l.Attribute("name").Value }
     .Union(
         l.Descendants("level2")
         .Select(l2=>"   " + l2.Attribute("name").Value)
      )
    );
foreach (var lv in lvs)
{
   result.AppendLine(lv);
}

Ps. Vous devez utiliser .Root sur l'une de ces versions.


Cela n'imprime-t-il pas tous les niveaux 2 après tous les niveaux 1?
sblom

@sblom oops, c'est vrai, je l'ai mis à jour avec ce que je voulais publier (j'ai fait un test contre, donc je suis sûr que ça marche maintenant :))
eglasius
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.