0

I have a large xml file with following structure.This is a snippet which contains many <xn:TestElement> nodes.

    <?xml version="1.0" encoding="utf-8" ?>
            <testDataFile
           xmlns="http://www.3gpp.org/ftp/specs/archive/32_series/32.615#configData"
           xmlns:xn="http://www.3gpp.org/ftp/specs/archive/32_series/32.625#genericNrm" xmlns:in="http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm">
          <fileHeader fileFormatVersion="32.615 V6.3" vendorName="TestVendor"/>
            <configData dnPrefix="">
    <xn:SubNetwork id="ONRM_ROOT_MO">
      <xn:SubNetwork id="RNC425">
        <xn:TestElement id="DA_Test_place0">
          <xn:attributes>
            <xn:userLabel>DA_Test_place0</xn:userLabel>
          </xn:attributes>
          <in:InventoryUnit id="n/a">
            <in:attributes>
              <in:manufacturerData>ProductName=Non-subrack HW,SlotCount=0</in:manufacturerData>
            </in:attributes>
            <in:InventoryUnit id="0">
              <in:attributes>
                <in:vendorUnitTypeNumber>KRC11876/1_R4A</in:vendorUnitTypeNumber>
                <in:manufacturerData>ProductName=RUS 02 B8</in:manufacturerData>
              </in:attributes>
            </in:InventoryUnit>
            <in:InventoryUnit id="0">
              <in:attributes>
                <in:vendorUnitTypeNumber>test/1_R4A</in:vendorUnitTypeNumber>
              </in:attributes>
            </in:InventoryUnit>
          </in:InventoryUnit>
          <in:InventoryUnit id="n/a">
            <in:attributes>
              <in:manufacturerData>ProductName=Virtual subrack,SlotCount=2</in:manufacturerData>
            </in:attributes>
            <in:InventoryUnit id="1">
              <in:attributes>
                <in:vendorUnitTypeNumber>KDU127174/4_R2D/A</in:vendorUnitTypeNumber>
              </in:attributes>
            </in:InventoryUnit>
            <in:InventoryUnit id="1">
              <in:attributes>
                <in:vendorUnitTypeNumber>KDU127174/4_R2D/B</in:vendorUnitTypeNumber>
                <in:manufacturerData>ProductName=RUS 02 B7</in:manufacturerData>
              </in:attributes>
            </in:InventoryUnit>
          </in:InventoryUnit>
        </xn:TestElement>
        <xn:TestElement id="DA_Test_place1">

        </xn:TestElement>
      </xn:SubNetwork>
    </xn:SubNetwork>
  </configData>
            </testDataFile>

Now I want to process this xml get information like:

testelementname     slotdata                                inventory unit number
------------------------------------------------------------------------------
DA_Test_place0      ProductName=Non-subrack HW,SlotCount=0  KRC11876/1_R4A
DA_Test_place0      ProductName=Non-subrack HW,SlotCount=0  test/1_R4A
DA_Test_place0      ProductName=Virtual subrack,SlotCount=2  KDU127174/4_R2D/A
DA_Test_place0      ProductName=Virtual subrack,SlotCount=2  KDU127174/4_R2D/B

How can I process this xml file and get information either in datatable or C# classes. I wrote the following code, but it got stuck with the hierarchy of xml

while (reader.Read())
{
    if (reader.Name != "xn:TestElement")
    {
        reader.ReadToFollowing("xn:TestElement");
    }

    while (reader.NodeType == XmlNodeType.Element && reader.LocalName == "TestElement")
    {
        XElement elements = (XElement)XElement.ReadFrom(reader);
        testelemenname = reader.GetAttribute("id");
        slotdata = GetInventoryValue(elements, "manufacturerData");
        invenotry unit number = GetInventoryValue(elements, "vendorUnitTypeNumber");
    }
}

private static string GetInventoryValue(XElement pin, string input)
{
    XElement manufacturerData = pin.Descendants().Where(a => a.Name.LocalName == input).FirstOrDefault();

    if (manufacturerData != null)
    {
        return (string)manufacturerData;
    }
}

EDIT

Heirarchy of XML changed a bit and added two level 'SubNetwork' node and one more node 'configData'and namespaces also changed,now i am not getting the result

peter
  • 8,158
  • 21
  • 66
  • 119

1 Answers1

1

You can achieve your desired result by using XDocument.

Here I created a sample console app for your demonstration purpose,

class Program
{
    public static void Main(string[] args)
    {
        XDocument doc = XDocument.Load(@"Path to your xml file");
        XNamespace ns = doc.Root.GetDefaultNamespace();
        XNamespace xdt = "http://www.3gpp.org";

        var result = doc.Descendants(ns + "TestElement")
        .Elements(ns + "InventoryUnit")
        .Elements(ns + "InventoryUnit")
        .Select(x => new
        {
            test_element_name = x.AncestorsAndSelf(ns + "TestElement").FirstOrDefault()?.Attribute("id")?.Value,
            slot_data = x.Ancestors(ns + "InventoryUnit").AncestorsAndSelf(ns + "InventoryUnit").FirstOrDefault().Element(ns + "attributes").Element(ns + "manufacturerData")?.Value,
            invenotry_unit_number = x.Element(ns + "attributes").Element(ns + "vendorUnitTypeNumber")?.Value,
        }).ToList();

        //-----------Print result--------------

        foreach (var item in result)
        {
            Console.WriteLine(item.test_element_name);
            Console.WriteLine(item.slot_data);
            Console.WriteLine(item.invenotry_unit_number);
            Console.WriteLine();
        }

        Console.ReadLine();
    }
}

Output:

enter image description here

Edit:

If your xml file size is too large and XDocument failed to parse it then you can try XmlSerializer like

XmlSerializer serializer = new XmlSerializer(new TestDataFile().GetType());
using (StringReader stringReader = new StringReader(File.ReadAllText(@"Path to your xml file")))
{
    TestDataFile testDataFile = (TestDataFile)serializer.Deserialize(stringReader);

     var result = testDataFile.ConfigData.SubNetwork.InnerSubNetwork.SelectMany(a => a.TestElement.SelectMany(x => x.InventoryUnit.SelectMany(y => y.IU
             .Select(z => new { test_element_name = x.Id, slot_data = y.Attributes.ManufacturerData, invenotry_unit_number = z.Attributes.VendorUnitTypeNumber })))).ToList();

    foreach (var item in result)
    {
        Console.WriteLine(item.test_element_name);
        Console.WriteLine(item.slot_data);
        Console.WriteLine(item.invenotry_unit_number);
        Console.WriteLine();
    }
}

And you need below class hierarchy to deserialize your xml,

[XmlRoot(ElementName = "fileHeader", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm")]
public class FileHeader
{
    [XmlAttribute(AttributeName = "fileFormatVersion")]
    public string FileFormatVersion { get; set; }
    [XmlAttribute(AttributeName = "vendorName")]
    public string VendorName { get; set; }
}

[XmlRoot(ElementName = "attributes", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm")]
public class Attributes
{
    [XmlElement(ElementName = "userLabel", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm")]
    public string UserLabel { get; set; }
    [XmlElement(ElementName = "manufacturerData", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm")]
    public string ManufacturerData { get; set; }
    [XmlElement(ElementName = "vendorUnitTypeNumber", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm")]
    public string VendorUnitTypeNumber { get; set; }
}

[XmlRoot(ElementName = "InventoryUnit", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm")]
public class InnerInventoryUnit
{
    [XmlElement(ElementName = "attributes", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm")]
    public Attributes Attributes { get; set; }
    [XmlAttribute(AttributeName = "id")]
    public string Id { get; set; }
}


[XmlRoot(ElementName = "InventoryUnit", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm")]
public class InventoryUnit
{
    [XmlElement(ElementName = "attributes", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm")]
    public Attributes Attributes { get; set; }
    [XmlAttribute(AttributeName = "id")]
    public string Id { get; set; }
    [XmlElement(ElementName = "InventoryUnit", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm")]
    public List<InnerInventoryUnit> IU { get; set; }
}

[XmlRoot(ElementName = "TestElement", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.625#genericNrm")]
public class TestElement
{
    [XmlElement(ElementName = "InventoryUnit", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm")]
    public List<InventoryUnit> InventoryUnit { get; set; }
    [XmlAttribute(AttributeName = "id")]
    public string Id { get; set; }
}

[XmlRoot(ElementName = "SubNetwork", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.625#genericNrm")]
public class InnerSubNetwork
{
    [XmlElement(ElementName = "TestElement", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.625#genericNrm")]
    public List<TestElement> TestElement { get; set; }
    [XmlAttribute(AttributeName = "id")]
    public string Id { get; set; }
}

[XmlRoot(ElementName = "SubNetwork", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.625#genericNrm")]
public class SubNetwork
{
    [XmlElement(ElementName = "SubNetwork", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.625#genericNrm")]
    public List<InnerSubNetwork> InnerSubNetwork { get; set; }
    [XmlAttribute(AttributeName = "id")]
    public string Id { get; set; }
}

[XmlRoot(ElementName = "configData", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.615#configData")]
public class ConfigData
{
    [XmlElement(ElementName = "SubNetwork", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.625#genericNrm")]
    public SubNetwork SubNetwork { get; set; }
    [XmlAttribute(AttributeName = "dnPrefix")]
    public string DnPrefix { get; set; }
}

[XmlRoot(ElementName = "testDataFile", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.615#configData")]
public class TestDataFile
{
    [XmlElement(ElementName = "fileHeader", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.615#configData")]
    public FileHeader FileHeader { get; set; }
    [XmlElement(ElementName = "configData", Namespace = "http://www.3gpp.org/ftp/specs/archive/32_series/32.615#configData")]
    public ConfigData ConfigData { get; set; }
    [XmlAttribute(AttributeName = "xmlns")]
    public string Xmlns { get; set; }
    [XmlAttribute(AttributeName = "xn", Namespace = "http://www.w3.org/2000/xmlns/")]
    public string Xn { get; set; }
    [XmlAttribute(AttributeName = "in", Namespace = "http://www.w3.org/2000/xmlns/")]
    public string In { get; set; }
}
er-sho
  • 9,581
  • 2
  • 13
  • 26
  • large xml will throw memory exception with xdoc rite? – peter Jan 31 '19 at 07:36
  • oh so how much its size appx? ok wait I'll update my answer with xml serializer. – er-sho Jan 31 '19 at 07:43
  • ?.Attribute("id")?.Value, – peter Jan 31 '19 at 09:36
  • just remove those `?` symbol from everywhere and let me know – er-sho Jan 31 '19 at 09:48
  • Heirarchy of XML changed a bit and added two level 'SubNetwork' node and one more node 'configData'and namespaces also changed,now i am not getting the result – peter Feb 03 '19 at 12:41
  • 1
    My answer was updated before your last update of your xml. So that your "SubNetwork" and "configData" not in my answer. Kindly wait, I'll update my answer with your last xml. – er-sho Feb 03 '19 at 17:08
  • But what about my answer with "XDocument" its also not worked for you? – er-sho Feb 03 '19 at 17:11
  • i didnot tried with xdocumen,thought may fail with memory exception at live environment.And more over i am using .net 4.0 – peter Feb 03 '19 at 17:54
  • While updating my answer with your updated xml, I found that your xml is not a valid so try to give final valid xml so I'll update my answer. check your updated xml to validate here => https://www.xmlvalidation.com/ – er-sho Feb 04 '19 at 05:53
  • no errors found ,thats what the result i found from that link.Probably since i changed xml namespace classes from new xml posted,, in the code namspace classes decorated have to change other wise will get error "There is an error in XML document (2, 2)." – peter Feb 04 '19 at 06:12
  • SubNetwork inside SubNetwork is always single or it may be a more than one? – er-sho Feb 04 '19 at 06:18
  • In the example User provided contains only one inner subnetwork,may be contains more than but not sure – peter Feb 04 '19 at 06:20
  • Answer updated successfully. Kindly notice this line => `var result = testDataFile....` and changed class hierarchy. :) – er-sho Feb 04 '19 at 06:30
  • It worked great,now i am comparing the herarchy which i created initially why it failed. I will update you – peter Feb 04 '19 at 06:40
  • Glad to hear. please mark the tick on left side of answer to make it green ans upvote by clicking up arrow to answer :) – er-sho Feb 04 '19 at 06:45
  • One small error, I have an attribute manufacturerData with same name in inner level also.I want get that value.And when i tried to add that attribute in 'Attributes' class.It throws error like "There was an error reflecting type ".How to get manufacturerData from both levels.I have edited my XML – peter Feb 04 '19 at 08:47
  • you are updating XML frequently so let me know this is your final xml? so I have to update it or you have more update on same xml – er-sho Feb 04 '19 at 09:12
  • Yea,this is the final xml template – peter Feb 04 '19 at 09:43
  • So there is no any change in class hierarchy but you can get that inner `manufacturer_data` like => `var r = testDataFile.ConfigData.SubNetwork.InnerSubNetwork.SelectMany(a => a.TestElement.SelectMany(x => x.InventoryUnit.SelectMany(y => y.IU .Select(z => new { test_element_name = x.Id, slot_data = y.Attributes.ManufacturerData, invenotry_unit_number = z.Attributes.VendorUnitTypeNumber, manu_data = z.Attributes.ManufacturerData })))).ToList();` – er-sho Feb 04 '19 at 10:24
  • Kingly notice I collected that inner `manufacturerData` in this variable => `manu_data = z.Attributes.ManufacturerData` in above code. you can change the name of this variable – er-sho Feb 04 '19 at 10:26
  • Glad to hear, please mark the tick to make it green and upvote also :) – er-sho Feb 04 '19 at 11:37
  • One more question i am having is,at some point of time if somebody changed the namespaces in the xml shared say here from http://www.3gpp.org/ftp/specs/archive/32_series/32.695#inventoryNrm to http://www.3gpp.org/ftp/specs/archive/32_series/32.695#gen123,Then how can we adjust this namespace change?is there any auto adjust or anywat we can skip these namespace issues – peter Feb 04 '19 at 13:12
  • 1
    It's better if you asked this question as a new post. So it possible to get best answer from any user. because I already spend too much time and effort to provide you a solution, but If I'll get some time then I'll definitely response you upon same. – er-sho Feb 04 '19 at 13:51
  • great i made it answer – peter Feb 05 '19 at 08:35