user219628
user219628

Reputation: 3905

Tricky Linq to XML

Please review below code and advice what needs to be done in order to get the additional last line in output as shown in expected output

class test {

   static void Main(string[] args)
    {

        XDocument doc = XDocument.Load("E:\\BI_analytics\\Data\\so.xml");
        var query = from test in doc.Descendants("tester")
                    from testreq in test.Descendants("testRequest")
                    from testresp in test.Descendants("testResponse")
                    let id = testreq.Element("id") == null ? string.Empty : testreq.Element("id").Value

                  //  select id;
                    from itm in testresp.Descendants("item")
                    select new
                    {
                        ID = (string)id,
                        Name = (string)itm.Attribute("itemname"),
                        Code = (string)itm.Attribute("itemocde"),
                    };


        foreach (var result in query)
        {
            Console.WriteLine(result);
        }
    }
}

Current output

{ ID = 2, Name = test item1, Code = 111 }
{ ID = 2, Name = test item2, Code = 222 }
{ ID = 3, Name = test item3, Code = 333 }
{ ID = 3, Name = test item4, Code = 444 }

Expected output

{ ID = 2, Name = test item1, Code = 111 }
{ ID = 2, Name = test item2, Code = 222 }
{ ID = 3, Name = test item3, Code = 333 }
{ ID = 3, Name = test item4, Code = 444 }
{ ID = 4, Name = , Code = }
<?xml version="1.0" encoding="utf-8"?>
<root>
  <tester>
    <testRequest>
      <id>2</id>
    </testRequest>
    <testResponse>
      <items>
        <item itemname="test item1" itemocde="111"/>
        <item itemname="test item2" itemocde="222"/>
      </items>
    </testResponse>
  </tester>
  <tester>
    <testRequest>
      <id>3</id>
    </testRequest>
    <testResponse>
      <items>
        <item itemname="test item3" itemocde="333"/>
        <item itemname="test item4" itemocde="444"/>
      </items>
    </testResponse>
  </tester>
  <tester>
    <testRequest>
      <id>4</id>
    </testRequest>
    <testResponse>
      <items />
    </testResponse>
  </tester>
</root>

Upvotes: 1

Views: 83

Answers (2)

Christopher Rathermel
Christopher Rathermel

Reputation: 935

I use a class to help me return values even if it is null that looks like this:

public static class LinqToXMLUtility
{
    /// <summary>
    /// Used to Get check the XElement Value and return 
    /// empty string if it is null (used for optional or missing xml items)
    /// </summary>
    /// <param name="pElement"></param>
    /// <param name="pstrElementName"></param>
    /// <returns></returns>
    public static string GetXElementValue(XElement pElement, string pstrElementName)
    {
        string strRet = string.Empty;
        try
        {
            XElement lElement = pElement.Element(pstrElementName);
            if (lElement != null)
            {
                strRet = lElement.Value;
            }
        }
        catch { }
        return strRet;
    }
}

And use it like so.

class test
{
    static void Main(string[] args)
    {
        XDocument doc = XDocument.Load("E:\\BI_analytics\\Data\\so.xml");
        var query = from test in doc.Descendants("tester")
                    from testreq in test.Descendants("testRequest")
                    from testresp in test.Descendants("testResponse")
                    let id = testreq.Element("id") == null ? string.Empty : 
                             testreq.Element("id").Value

                    //  select id;
                    from itm in testresp.Descendants("item")
                    select new
                    {
                        ID = LinqToXMLUtility.GetXElementValue(itm, "id"),
                        Name = LinqToXMLUtility.GetXElementValue(itm, "itemname"),
                        Code =  LinqToXMLUtility.GetXElementValue(itm, "itemocde"),
                    };


        foreach (var result in query)
        {
            Console.WriteLine(result);
        }
    }
}

Upvotes: 3

Jon Skeet
Jon Skeet

Reputation: 1500505

This is the problem:

from itm in testresp.Descendants("item")

You don't have any item elements, so you probably want:

from itm in testresp.Descendants("item").DefaultIfEmpty()

... at which point you need:

select new
{
    ID = (string)id,
    Name = itm == null ? "" : (string)itm.Attribute("itemname"),
    Code = itm == null ? "" : (string)itm.Attribute("itemocde"),
};

Upvotes: 2

Related Questions