Flatten complex XDocument without knowing DOM

Sample XML:

<Pricing>
  <PriceGuide id="e4c3db5c">
    <Name>Price Guide A</Name>
    <Products>
      <Product id="1">
        <Name>Product 1</Name>
        <Prices>
          <Price>
            <Region id="40">Chicago</Region>
            <PriceLow>48</PriceLow>
            <PriceHigh>52</PriceHigh>
            <UnitOfMeasure>MT</UnitOfMeasure>
          </Price>
          <Price>
            <Region id="71">Dallas</Region>
            <PriceLow>45.5</PriceLow>
            <PriceHigh>47</PriceHigh>
            <UnitOfMeasure>MT</UnitOfMeasure>
          </Price>
        </Prices>
      </Product>
      <Product id="2">
        <Name>Product 2</Name>
        <Prices>
          <Price>
            <Region id="40">Chicago</Region>
            <PriceLow>48</PriceLow>
            <PriceHigh>49</PriceHigh>
            <UnitOfMeasure>MT</UnitOfMeasure>
          </Price>
          <Price>
            <Region id="101">Los Angeles </Region>
            <PriceLow>43</PriceLow>
            <PriceHigh>45</PriceHigh>
            <UnitOfMeasure>MT</UnitOfMeasure>
          </Price>
          <Price>
            <Region id="71">Dallas</Region>
            <PriceLow>45.5</PriceLow>
            <PriceHigh>48.5</PriceHigh>
            <UnitOfMeasure>MT</UnitOfMeasure>
          </Price>
        </Prices>
      </Product>
    </Products>
  </PriceGuide>
</Pricing>

Expected Result: (data written to a CSV file or dumped into a DataTable)

Price Guide A, Product 1, Chicago, 48, 52, MT
Price Guide A, Product 1, Dallas, 45.5, 47, MT
Price Guide A, Product 2, Chicago, 48, 49, MT
Price Guide A, Product 2, Los Angeles, 43, 45, MT
Price Guide A, Product 2, Dallas, 45.5, 48.5, MT

Main Issue: I basically get an unknown XML file that I have to show as a flat table. This is an example of one of the many files I could have to process. I do not know the DOM ahead of time so I cannot do a straight LINQ query with given node names. I tried an attempt at walking the the DOM recusively, but when you are in the recursion it is difficult to know when it is time to write out a record. Extra Credit: From the example, sometimes there are attributes on a node. If there is the attribute "id" I would like to include the value for that in the output. In this case my output would be:

e4c3db5c, Price Guide A, 1, Product 1, 40, Chicago, 48, 52, MT
e4c3db5c, Price Guide A, 1, Product 1, 71, Dallas, 45.5, 47, MT
e4c3db5c, Price Guide A, 2, Product 2, 40, Chicago, 48, 49, MT
e4c3db5c, Price Guide A, 2, Product 2, 101, Los Angeles, 43, 45, MT
e4c3db5c, Price Guide A, 2, Product 2, 71, Dallas, 45.5, 48.5, MT

Thanks in advance.

Edit: The following works, but requires me to know the XML structure ahead of time. I am looking to generalize this code:

var details =
from level1 in _xmlDoc.Root.Elements("PriceGuide")
from level2 in level1.Elements("Name")
from level3 in level2.Elements("Products")
from level4 in level3.Elements("Product")
from level5 in level4.Elements("Name")
from level6 in level5.Elements("Prices")
from level7 in level6.Elements("Price")
from level8a in level7.Elements("Region")
from level8b in level7.Elements("PriceLow")
from level8c in level7.Elements("PriceHigh")
from level8d in level7.Elements("UnitOfMeasure")
select new
{
                PriceGuideId = (string)level1.Attribute("id"),
                PriceGuideName = (string)level2.Value,
                ProductId = (string)level3.Attribute("id"),
                ProductName = (string)level4.Value,
                RegionId = (string)level8a.Attribute("id"),
                RegionName = (string)level8a.Value,
                PriceLow = (string)level8b.Value,
                PriceHigh = (string)level8c.Value,
                UnitOfMeasure = (string)level8d.Value,
};

I know it doesn't help much.

Answers


I don't know how to do it in linq. Here is a quick and dirty code that works

    XmlDocument dom = new XmlDocument();
    dom.LoadXml("<Pricing><PriceGuide id=\"e4c3db5c\"><Name>Price Guide A</Name><Products><Product id=\"1\"><Name>Product 1</Name><Prices><Price><Region id=\"40\">Chicago</Region><PriceLow>48</PriceLow><PriceHigh>52</PriceHigh><UnitOfMeasure>MT</UnitOfMeasure></Price><Price><Region id=\"71\">Dallas</Region><PriceLow>45.5</PriceLow><PriceHigh>47</PriceHigh><UnitOfMeasure>MT</UnitOfMeasure></Price></Prices></Product><Product id=\"2\"><Name>Product 2</Name><Prices><Price><Region id=\"40\">Chicago</Region><PriceLow>48</PriceLow><PriceHigh>49</PriceHigh><UnitOfMeasure>MT</UnitOfMeasure></Price><Price><Region id=\"101\">Los Angeles </Region><PriceLow>43</PriceLow><PriceHigh>45</PriceHigh><UnitOfMeasure>MT</UnitOfMeasure></Price><Price><Region id=\"71\">Dallas</Region><PriceLow>45.5</PriceLow><PriceHigh>48.5</PriceHigh><UnitOfMeasure>MT</UnitOfMeasure></Price></Prices></Product></Products></PriceGuide></Pricing>");

    List<KeyValuePair<int, String>> result = FlattenXML(dom.DocumentElement, "", 0);
    var q = result.Where(c => c.Key == result.Max(b => b.Key)).Select(b => b.Value.Substring(0, b.Value.Length - 1)).ToArray();

    Console.WriteLine(String.Join(System.Environment.NewLine, q));

    private List<KeyValuePair<int, String>> FlattenXML(XmlElement node, String parent, int level)
    {
        List<KeyValuePair<int, String>> result = new List<KeyValuePair<int, String>>();
        String detail = "";

        if (node.HasAttribute("id"))
            parent += node.Attributes["id"].InnerText + ",";

        if (node.InnerText == node.InnerXml && node.InnerText != "")
        {
            parent += node.InnerText + ",";
        }

        foreach (XmlElement child in node.ChildNodes)
        {
            if (child.InnerText == child.InnerXml && child.InnerText != "")
            {
                detail += child.InnerText + ",";
                level++;
            }

            if (child.FirstChild != child.LastChild)
            {
                List<KeyValuePair<int, String>> childResult = FlattenXML(child, parent + detail, level);
                result.AddRange(childResult);
            }
        }
        result.Add(new KeyValuePair<int, String>(level, parent + detail));
        return result;
    }

Need Your Help

To get a particular CSS property from a CSS file thorugh Javascript

javascript html css

I need a technique to get the value from a CSS file through a javascript function.

Mutator methods with an Array of Objects in Java

java arrays object

So i have an array of Room objects, the reason its static is because I'm doing it in main.