0

I asked this question earlier and I got very quick and helpful responses: Linq to XML query not picking up any values

I followed the advice as given in the answer marked as correct which worked and allowed me to pick up the 'Product' tags. I am now searching within that 'Product' tag in order to find values to import into my database and I'm a bit stuck again.

Here is the structure of my XML file:

<?xml version="1.0"?>
<!DOCTYPE ONIXMessage SYSTEM "http://www.editeur.org/onix/2.1/reference/onix-international.dtd">
<ONIXMessage xmlns="http://www.editeur.org/onix/2.1/reference" release="2.1">
  <Header>
    <FromCompany>MyCo</FromCompany>
    <FromPerson>Joe Bloggs</FromPerson>
    <FromEmail>[email protected]</FromEmail>
    <SentDate>20120522</SentDate>
  </Header>
  <Product>
    <ProductForm>DG</ProductForm>
    <Title>
      <TitleType>01</TitleType>
      <TitleText>Blogg</TitleText>
    </Title>
    <WorkIdentifier>
      <WorkIDType>15</WorkIDType>
      <IDValue>PI3564231</IDValue>
    </WorkIdentifier>
    <Language>
      <LanguageRole>01</LanguageRole>
      <LanguageCode>eng</LanguageCode>
    </Language>
  </Product>
</ONIXMessage>

Here is the snippet from my code:

        XElement onix = XElement.Load(fs);

        // Get all the product information.
        //
        var products = onix.Descendants().Where(m => m.Name.LocalName == "Product").ToList();

        foreach (var p in products)
        {
            try
            {
                prod.Title = p.Elements("Title").First(t => (t.Element("TitleType").Value == "01")).Element("TitleText").Value;
                //further processing
            }
            catch (Exception exp)
            {
                throw new FileProcessingException("Unable to process product.", exp);
            }
        }

When I search for the Title text as shown above, the exception is caught. The exception is "Object reference not set to an instance of an object." The item is clearly in the XML file, it just isn't picking it up right.

I had never seen .Descendants() before toda and I get what it means, but from what I read I should be able to use .Elements() here also?

Can anyone see what could be wrong?

5
  • 2
    Can you tell us where exactly the error is occurring and what the Exception says? Commented Jun 14, 2012 at 13:18
  • Also, .First() is tricky because it will throw an Exception if nothing is found. .FirstOrDefault() will return Null if nothing is found, which can still cause a NullReferenceException if you don't test for Null. Commented Jun 14, 2012 at 13:19
  • If you put a breakpoint on the line that's failing and evaluate p.Element("Title"), does it contain any elements? If you evaluate p.Elements("Title").First(t => (t.Element("TitleType").Value == "01")) is that where exception comes from? Commented Jun 14, 2012 at 13:20
  • @Cyborgx37 - just edited my post with the exception... Commented Jun 14, 2012 at 13:29
  • Sounds like you need to step through your code and do some testing. Set breakpoints and look for the source of your error. Your question is basically to have us hunt for the bug in your code, which isn't really a technical question. Also, read the documentation for XML at MSDN. msdn.microsoft.com/en-us/library/bb387098.aspx Commented Jun 14, 2012 at 13:43

2 Answers 2

1

Well, as I answered your previous question, I think I might help...

Your document has a namespace (xmlns). If you wanna use Elements("tagName") or Element("tagName"), etc, you have to prefix all your TagName By the namespace.

so your query will work (as mentioned by Habib.OSU in your previous question), if you do

  XNamespace ns = onix.Attribute("xmlns").Value;
  var products2 = onix.Elements(ns + "Product").ToList();

  foreach (var p in products2)
  {
        var title = p.Elements(ns + "Title")
        .First(t => (t.Element(ns + "TitleType").Value == "01"))
        .Element(ns + "TitleText").Value;
  }

if you absolutely don't wanna use the namespace, you have to use Descendants() and Name.LocalName. It'll give you the same, but... it's really hard to read.

 var products = onix.Descendants().Where(m => m.Name.LocalName == "Product").ToList();

 foreach (var p in products)
 {

      var title = p.Descendants().Where(m => m.Name.LocalName == "Title")
         .Descendants()
         .First(m => m.Name.LocalName == "TitleType" && m.Value == "01")
         .Parent
         .Descendants()
         .First(m => m.Name.LocalName == "TitleText")
         .Value;

 }

I didn't put any NULL Check, this would be, of course, something to do.

Also note that the second query will be less performant, as Descendants() enumerates all subElements of an elements, without taking care of hierarchy. While Elements() will retrieve the direct child Nodes only.

Sign up to request clarification or add additional context in comments.

Comments

0

Try this:

    XDocument doc = XDocument.Load(fs);
 var products= from elements in doc.Elements("ONIXMessage").Elements("Product");
   foreach (var p in products)
            {
                    var title  = (from items in p.Elements("Title")
                                 where items.Element("TitleType").Value == "01"
                                 select items.Element("TitleText").Value).FirstOrDefault();
            }

1 Comment

thanks for the response. I tried your above example, the exception is no longer thrown however the field is now just populated with 'null', so for whatever reason, the linq is not picking up the XML correctly.

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.