views:

383

answers:

2

Hello everyone. I'm wrestling to deserialize the following XML:

<?xml version="1.0" encoding="utf-8" ?>

<conf name="settings">

    <item name="lorem"
     one="the"
     two="quick"
     three="brown"
     four="fox"
     five="jumps"
     six="over"
     seven="the"
     eight="lazy"
     nine="dog"
      />

    <item name="ipsum"
     one="how"
     two="many"
     three="roads"
     four="must"
     five="a"
     six="man"
     seven="walk"
     eight="down"
     nine="?"
      />

</conf>

hoping to do so in the most elegant and succinct way using LINQ-to-XML, but given that I'm not the smartest kid in town when it comes to nested methods, infered types, generics, et cétera, I thought it'd be a good idea to ask if any of you guys would like to go ahead and show me some LINQ literacy :)

Right now for every value I'm doing something like:

XDocument config = XDocument.Load("whatever.conf");

var one = from q in config.Descendants("item")
            select (string)q.Attribute("one");

var two = from q in config.Descendants("item")
            select (string)q.Attribute("two");

And I do know I'm totally missing the point, not only because I'm repeating myself a lot there but also because that queries only work when there's only one item so, again if you have any comment or suggestion it would be really appreciated. Thanks much in advance!

UPDATE: in case that perhaps the former example wasn't really helpful, here's a more sort of realistic scenario:

<?xml version="1.0" encoding="utf-8" ?>

<conf name="ftp-settings" freq="daily" time="23:00">
    <item name="isis"
      host="10.10.1.250"
      user="jdoe"
      pass="4/cB0kdFGprXR/3oTs8mtw=="
      file="backup.tar.gz"
      path="/var/log"
     />
    <item name="seth"
      host="10.10.2.250"
      user="jdoe"
      pass="4/cB0kdFGprXR/3oTs8mtw=="
      file="backup.tar.gz"
      path="/var/log"
     />
</conf>

Therefore for each of those items I'd like to generate variables so I can pass them as parameters for some FTP management method.

SOLVED:

It was as easy as doing a foreach:

var elements = from element in xml.Descendants("item") select element;

foreach (XElement item in elements) {
    ftp.DownloadFile(
     item.Attribute("host").Value,
     item.Attribute("user").Value,
     item.Attribute("pass").Value,
     item.Attribute("file").Value,
     item.Attribute("path").Value
     );
}
+1  A: 

Normally I'd expect to want a single representation of each element, rather than one sequence of "one" values and another sequence of "two" values. In other words, I'd expect something like:

var items = config.Descendants("item")
                  .Select(element => Item.FromXElement(element));

If you have a lot of attributes in a single element, I find it helpful to separate out the "make an object from this element" code into its own method (in this case Item.FromXElement) rather than stuff it all into the query. If Item shouldn't know about its LINQ to XML representation, put the method somewhere that should :)

items is then an IEnumerable<Item> which you can convert into a list or whatever else you want to do.

Of course it really depends on what you're trying to do - and we don't really know what this is meant to represent. Maybe you really do want each "attribute sequence" separately...


Item.FromXElement would look something like:

public static Item FromXElement(XElement element)
{
    string name = (string) element.Attribute("name");
    string host = (string) element.Attribute("host");
    // etc
    return new Item(name, host, ...);
}

(Where obviously you'd create a constructor with the appropriate parameters.)

Jon Skeet
First all thanks a lot for your comment Jon! I really need to keep those separate; Imagine I'm persisting, let's say, connection details or, bookmarks, so on, so forth, then it's really up to keeping the *lorem* and *ipsum* elements separate, and then creating key/value pairs out of its attributes. I hope you followed my elaboration, anyhow don't hesitate to ask me for further explanatory efforts :)
Nano Taboada
I don't *really* follow the elaboration, no. Could you edit your question to give a fuller example of what you're trying to do and how you want to use the information afterwards?
Jon Skeet
I did. Thanks much in advance Jon.
Nano Taboada
Right - that looks like each item has a bunch of information which goes together. I'd create appropriate objects with all of it together, and a list of those objects.
Jon Skeet
Any idea about how to implement the **Item.FromXElement** serialization magic?
Nano Taboada
That bit's easy - just check the attributes you're interested in. Editing...
Jon Skeet
+1  A: 

I would do this:

XDocument config = XDocument.Load("whatever.conf");

var pairs = from q in config.Descendants("item")
           select new {
               One = (string)q.Attribute("one"),
               Two = (string)q.Attribute("two") };

var ones = pairs.Select(pair => pair.One);
var twos = pairs.Select(pair => pair.Two);
Pavel Minaev
Pavel thanks much for the comment, although what I really need is to have two resulting collections isis and seth, both of them with all its attributes accessible perhaps in the form of isis.host, isis.user, so on.
Nano Taboada