0

This question is a little out there. Is there a way, other than a triple nested List, to declare attributes to a List element?

Context: I have a List of PLCs where each PLC has 4 "attribute" Elements and a List of Tags.

Ex: I know this is wrong, just trying to illustrate the idea.

List<List<string>> plcTagInfo;
plcTagInfo[0][0] = PLC Name
plcTagInfo[0][1] = IP address of aforementioned PLC
plcTagInfo[0][2] = protocol used to communicate with PLC.
plcTagInfo[0][3] = processor type of aforementioned PLC.
plcTagInfo[0][4] = List of ALL tags inside of that PLC and each tag has 4 elements.
plcTagInfo[1] = new plc to cycle through.

In which I will cycle through each tag and tag attribute.

Tag Elements are: tagName(string), dataType(string = uint32), elemSize(string = "4"), elemCount(string = "10") as an example.

Is there an easy route to go about this? Or should I just split the plcs into one list and the tags into a separate list and associate the two incrementally?

Just in case I wasn't clear enough. Here is the XML that I am importing the data from.

<?xml version="1.0" encoding="utf-8"?>
<PLCS>
  <PLC Name="Test">
    <PLCInfo IPAddress="192.168.1.60" ProcessorType="LGX" Protocol="ab_eip" />
    <Tags>
      <Tag TagName="TagName1" DataType="uint8" ElemSize="4" ElemCount="10" />
      <Tag TagName="TagName2" DataType="uint8" ElemSize="4" ElemCount="1" />
      <Tag TagName="TagName3" DataType="uint8" ElemSize="4" ElemCount="1" />
      <Tag TagName="TagName4" DataType="uint8" ElemSize="4" ElemCount="1" />
      <Tag TagName="TagName5" DataType="uint8" ElemSize="4" ElemCount="1" />
      <Tag TagName="TagName6" DataType="uint8" ElemSize="4" ElemCount="1" />
      <Tag TagName="TagName" DataType="uint8" ElemSize="4" ElemCount="10" />
    </Tags>
  </PLC>
  <PLC Name="Test2">
    <PLCInfo IPAddress="192.168.1.30" ProcessorType="LGX" Protocol="ab_eip" />
    <Tags>
      <Tag TagName="Tagname1" DataType="uint8" ElemSize="4" ElemCount="10" />
      <Tag TagName="Tagname2" DataType="uint8" ElemSize="4" ElemCount="10" />
      <Tag TagName="Tagname3" DataType="uint8" ElemSize="4" ElemCount="10" />
      <Tag TagName="Tagname4" DataType="uint8" ElemSize="4" ElemCount="10" />
      <Tag TagName="Tagname5" DataType="uint8" ElemSize="4" ElemCount="10" />
      <Tag TagName="Tagname6" DataType="uint8" ElemSize="4" ElemCount="10" />
      <Tag TagName="Tagname7" DataType="uint8" ElemSize="4" ElemCount="10" />
      <Tag TagName="Tagname8" DataType="uint8" ElemSize="4" ElemCount="10" />
      <Tag TagName="TagName1" DataType="uint8" ElemSize="4" ElemCount="10" />
    </Tags>
  </PLC>
</PLCS>

Muchos Dankeschön!

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
NM24
  • 39
  • 6

4 Answers4

2

Make an object that defines the properties you care about grouping together, then create a List of that object.

Daniel Mann
  • 57,011
  • 13
  • 100
  • 120
2

Instead of parsing the xml into a list, create a class that corresponds with the XML and then deserialize it. For an example, see this answer.

Community
  • 1
  • 1
CC Inc
  • 5,842
  • 3
  • 33
  • 64
  • Haven't experienced doing anything this way before. Time to learn something new! Thank you! – NM24 Dec 10 '16 at 02:12
1

I've got a pretty decent workflow that works well when I need to deal with XML. Visual Studio has some nice tools built-in to handle XML, and some steps just need some massaging to get it right.

One caveat for this - the XML has to be something that validatable by an XSD.


If you already have an XSD for your XML you can skip steps 1-2)

  1. Run the document through xsd.exe. You can run this tool through the Developer Command Prompt for Visual Studio. (Typically location is in the Visual Studio folder in the start menu. Typical execution of this command is xsd.exe MyXMLFile.xml. The output will be a file MyXMLFIle.xsd
  2. Massage the XSD file. In the case of your XML, it generated a schema that allows multiple <Tags /> and <PLCInfo /> elements to be present, which I believe to be incorrect, so I adjuted the schema appropriately.

    <?xml version="1.0" encoding="utf-8"?>
    <xs:schema id="PLCS" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
      <xs:element name="PLCS" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
        <xs:complexType>
          <xs:choice minOccurs="0" maxOccurs="unbounded">
            <xs:element name="PLC">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="PLCInfo" minOccurs="1" maxOccurs="1">
                    <xs:complexType>
                      <xs:attribute name="IPAddress" type="xs:string" />
                      <xs:attribute name="ProcessorType" type="xs:string" />
                      <xs:attribute name="Protocol" type="xs:string" />
                    </xs:complexType>
                  </xs:element>
                  <xs:element name="Tags" minOccurs="1" maxOccurs="1">
                    <xs:complexType>
                      <xs:sequence>
                        <xs:element name="Tag" minOccurs="0" maxOccurs="unbounded">
                          <xs:complexType>
                            <xs:attribute name="TagName" type="xs:string" />
                            <xs:attribute name="DataType" type="xs:string" />
                            <xs:attribute name="ElemSize" type="xs:string" />
                            <xs:attribute name="ElemCount" type="xs:string" />
                          </xs:complexType>
                        </xs:element>
                      </xs:sequence>
                    </xs:complexType>
                  </xs:element>
                </xs:sequence>
                <xs:attribute name="Name" type="xs:string" />
              </xs:complexType>
            </xs:element>
          </xs:choice>
        </xs:complexType>
      </xs:element>
    </xs:schema>
    
  3. Run the xsd file through xsd.exe again, this time specifying to generate code. Typical execution is xsd.exe MyXMLFile.xsd /c /n:Example.Model. The output will be a C# source code file, MyXMLFile.xs.

  4. Add the generated .cs file to your project.
  5. Instantiate an XMLSerializer for the root type of the XML. In your document's case, this was PLCS. In the example, below, I have the XML is a string which I passed to a StringReader to feed the Deserialize() method.

    using Example.Model;
    
    // ... later
    
    XmlSerializer serializer = new XmlSerializer(typeof(PLCS), new XmlRootAttribute{ ElementName = "PLCS" });
    var reader = new StringReader(textXml);
    PLCS info = (PLCS) serializer.Deserialize(reader);
    
  6. From there, just work with it like any other POCO. Here's something to outputs it to the console.

    foreach (var plc in info.Items)
    {
        Console.WriteLine(plc.Name);
        Console.Write($"{plc.PLCInfo.IPAddress} {plc.PLCInfo.ProcessorType} {plc.PLCInfo.Protocol}\r\n");
        foreach (var tag in plc.Tags)
        {
            Console.WriteLine($"\t[{tag.TagName}] {tag.DataType} {tag.ElemSize} {tag.ElemCount}");
        }
    }
    
jdphenix
  • 15,022
  • 3
  • 41
  • 74
  • Oh wow. This looks extremely useful! I'll be rewriting the entire program to clean and make more efficient. I'll use this method on that go around. Thank you! – NM24 Dec 10 '16 at 03:15
0

using xml linq

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);
            var results = doc.Descendants("PLC").Select(x => new {
                name = (string)x.Attribute("Name"),
                ipAddress = (string)x.Element("PLCInfo").Attribute("IPAddress"),
                tags = x.Descendants("Tag").Select(y => new {
                        name = (string)y.Attribute("TagName"),
                        type = (string)y.Attribute("DataType"),
                        size = (int)y.Attribute("ElemSize"),
                        count = (int)y.Attribute("ElemCount")
                }).ToList()
            }).ToList();

        }
    }
}
jdweng
  • 33,250
  • 2
  • 15
  • 20