0

I have two xml files which I am comparing with each other. The Linq query result1 is throwing Null Reference Exception after executing correctly for one rule. And when I debugged I found the section is displaying wrong values. I am unable to figure out the cause.

Rules.xml file:

<rule id="1" numberofsections="2">
  <section id="1" attributeid="1686" ruleoperator="=="  condition="and">
    <name>Processor type</name>
    <value>Core i3</value>
  </section>
  <section id="2" attributeid="1438" ruleoperator="&lt;" condition="and" >
    <name>Weight</name>
    <value>3.8 LBS</value>
  </section>
  <type>ultrabook</type>
</rule> 

And the code snippet:

XDocument rulesXml = XDocument.Load("/RulesEnginescope/RulesEnginescope/rulesSubType.xml");
XDocument productXml = XDocument.Load("c:/RuleEngine/RuleEngine/product.xml");
var getSelectedLeafCategoryRules = from rules2 in       rulesXml.Descendants("QueryTransformation").Descendants("leafcategory")
                                       where ((long)System.Convert.ToDouble(rules2.FirstAttribute.Value) == 4590)
                                       select rules2;

    var rules = getSelectedLeafCategoryRules.Descendants("rule");
    var productAttribute = productXml.Descendants("AttrList").Descendants("Attr");

    foreach (var x in rules)
    {
        var section = x.Elements("section");          
       /*Wrong value in section.count()*/ 
        Console.WriteLine(section.Count());
       var result1 = from p in section
                      from pa in productAttribute
                      where (p.Attribute("attributeid").Value == pa.Attribute("id").Value
                       && p.Element("name").Value == pa.Element("Name").Value)
                      select new
                      {
                          ruleAttribute = new
                          {
                              ruleId = p.Attribute("attributeid").Value,
                              ruleOperator = p.Attribute("ruleoperator").Value,
                              name = p.Element("name").Value,
                              value = p.Element("value").Value,
                              condition = p.Attribute("condition").Value
                          },
                          prodAttribute = new
                          {
                              productId = pa.Attribute("id").Value,
                              name = pa.Element("Name").Value,
                              value = pa.Element("ValueList").Element("Value").Value
      /*Error*/                    }

                      };

        if (result1.Count() != 0 && result1.Count() == System.Convert.ToInt64(x.Attribute("numberofsections").Value))
        {
            //checking each section
            foreach (var r in result1)
            {
                ...
            }

    }
ssingh
  • 237
  • 1
  • 4
  • 15
  • Almost all cases of `NullReferenceException` are the same. Please see "[What is a NullReferenceException in .NET?](http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-in-net)" for some hints. – John Saunders Sep 05 '13 at 23:40
  • Most likely an element or an attribute is missing. You can use `(string)p.Element("name") ?? ""` or `(string)p.Attribute("ruleoperator") ?? ""` to safely handle this situation. – Tim Sep 05 '13 at 23:44
  • small spelling mistakes cost a lot when using XML.. The error was because of spelling mistake in one element. – ssingh Sep 06 '13 at 11:14

1 Answers1

2

The idiomatic way to get the value of elements and attributes in LINQ-to-XML is to cast the element or attribute to the type you want, rather than accessing the Value attribute.

prodAttribute = new
                {
                   productId = (string)pa.Attribute("id"),
                   name = (string)pa.Element("Name"),
                   // ...
                }

Using this pattern avoids null ref exceptions caused when calls to Attribute() and Element() don't find a matching node. It also reduces verbosity:

((long)System.Convert.ToDouble(rules2.FirstAttribute.Value)
// should be 
(long)rules2.FirstAttribute

You'll still need to add null checks when you're accessing children of children. This can get verbose; one way to keep it succinct is to use IEnumerable-oriented methods so that you're operating on a (possibly empty) collection, rather than a (possibly null) instance.

pa.Element("ValueList").Element("Value").Value
// could be
(string)pa.Elements("ValueList").Elements("Value").FirstOrDefault ()

Finally, note that capitalization matters in LINQ-to-XML. In your code you seem to be switching capitalization patterns ("id" vs. "Name") often; it's likely that your source XML is more consistent.

Steve Ruble
  • 3,625
  • 17
  • 27