0

Is there any better way doing this? I have to get both property values. XML always has only these 2 properties.

My xml:

<Template name="filename.txt">
  <Property name="recordSeparator">\r\n</Property>
  <Property name="fieldCount">16</Property>
</Template>

Linq:

            var property = from template in xml.Descendants("Template")
                       select new
                                  {
                                      recordDelim = template.Elements("Property").Where(prop => prop.Attribute("name").Value == "recordSeparator")
                                                    .Select(f => new { f.Value }),
                                      fieldCount = template.Elements("Property").Where(prop => prop.Attribute("name").Value == "fieldCount")
                                                    .Select(f => new { f.Value })
                                  };
3
  • Please clarify your question to explain what you're trying to do. Read tinyurl.com/so-hints Commented Jun 27, 2011 at 6:24
  • Are you expecting multiple delimiters or fieldCount properties in a single template? Because, Select() returns IEnumerable<T>, in your case IEnumerable<String>. That means that both recordDelim and fieldCount will be of type IEnumerable<String>. Is that what you need in your anonymous object? Commented Jun 27, 2011 at 6:27
  • @kornelijepetak, xml is always same and has only these 2 properties. Commented Jun 27, 2011 at 6:28

2 Answers 2

3

"Better way" depends on what exactly are you trying to achieve - performance, simplicity, etc.?

I guess I would create a class that contains what you are trying to get with anonymous classes.

public class Item {
    public String Separator { get; set; }
    public int FieldCount { get; set; }
}

and then I would modify the LINQ to:

var templates = from template in xml.Descendants("Template")
                let children = template.Elements("Property")
                select new Item() {
                    Separator = children.First(tag=>tag.Attribute("name").Value == "recordSeparator").Value,
                    FieldCount = Int32.Parse(children.First(tag=>tag.Attribute("name").Value == "fieldCount").Value)
                };

List<Item> items = templates.ToList();

Note that this will cause NullReference exception in case your Template tag does not contain two Property tags, each with specified attributes.

Also it will throw an exception in parsing the integer from a FieldCount if it's not a number.

Idea:

If the xml generated is your own, and you can change it's format, why not do something like:

<Template>
  <Name>filename.txt</Name>
  <RecordSeparator>\r\n</RecordSeparator>
  <FieldCount>16</FieldCount>
</Template>

It's easier to read and to parse, and it's a little bit shorter.

In the end, I think this is how I would do it:

Having this class:

public class Item 
{
   public String FileName { get; set; }
   public String Separator { get; set; }
   public int FieldCount { get; set; }
}

and this private method:

private Item GetItemFromTemplate(XElement node) 
{
    return new Item() {
        FileName = node.Element("Name").Value,
        Separator = node.Element("RecordSeparator").Value,
        FieldCount = Int32.Parse(node.Element("FieldCount").Value)
    }; 
}    

I could do in code:

XDocument doc = XDocument.Load("myfile.txt");

List<Item> items = (from template in doc.Elements("Template")
                   select GetItemFromTemplate(template)).ToList();
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the idea! I think i am going to change the structure of the xml because it really is easier to read and parse.
you might want to consider the edit I've made. To simplify the LINQ query.
Well, you put quite some effort to the answer :). Although concurring answer +1.
@hs2d. My asnwers are never perfect. :) I always find a way to improve my solutions :) I'm glad you find it helpful.
1

This one is a little more efficient:

var properties =
    from template in xml.Descendants("Template")
    let propertyNodes = template.Elements("Property")
        .Select(arg => new { Name = arg.Attribute("name").Value, Value = arg.Value })
    select
        new
        {
            recordDelim = propertyNodes.Single(arg => arg.Name == "recordSeparator").Value,
            fieldCount = propertyNodes.Single(arg => arg.Name == "fieldCount").Value
        };

If you have always one Template node:

var propertyNodes = xml.XPathSelectElements("/Template/Property")
    .Select(arg => new { Name = arg.Attribute("name").Value, arg.Value })
    .ToList();

var properties =
    new
    {
        recordDelim = propertyNodes.Single(arg => arg.Name == "recordSeparator").Value,
        fieldCount = propertyNodes.Single(arg => arg.Name == "fieldCount").Value
    };

2 Comments

This, of course works, but I wouldn't force anonymous objects, unless you need this info (delimiter, count) only within the method you are doing the LINQ query. Otherwise, I think it would be smart (and maybe even neccessary) to go with the custom created class.
@kornelijepetak - 100% agree.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.