0

By necessity, I have been getting more into the System.XML aspect of VB 2010. In parsing through certain XML files produced by equipment used by our field guys, I'm trying to extract tag names and their inner text. That's really no problem, but I need to determine the level of descendancy for each child node in order to present the results in a textbox in a somewhat formatted way. For example, from the XML code below:

<document>
    <PointRecord ID="00000050" TimeStamp="2017-03-03T09:39:54">
        <Name>WF2510</Name>
        <Code>EOC RECT 6/</Code>
        <Method>StaticObservation</Method>
        <Classification>Normal</Classification>
        <Deleted>false</Deleted>

        <ECEFDeltas>
            <DeltaX>
              <Value>-14179.040909261</Value>
              <InTolerance>True</InTolerance>
            </DeltaX>
            <DeltaY>
              <Value>-3572.6636230592</Value>
              <InTolerance>True</InTolerance>
            </DeltaY>
            <DeltaZ>
              <Value>-8319.8607607852</Value>
              <InTolerance>False</InTolerance>
            </DeltaZ>
          </ECEFDeltas>
    </PointRecord>
</Document>

I would like to extract the node names and inner text to present it in a more readable format for the non-XML oriented, like this:

Point Record ID: 00000050 Time Stamp: 2017-03-03 09:39:54
  Name: WF2510
  Code: EOC RECT 6
  Method: StaticObservation
  Classification: Normal
  Deleted: False
  ECFDeltas-
    DeltaX: -14179.040909261 In Tolerance: Yes
    DeltaY: -3572.6636230592 In Tolerance: Yes
    DeltaZ: -8319.8607607852 In Tolerance: No

The problem I am having is knowing how to group the results contained in each node in sub-levels in order to indent the names and values from the child nodes in each descendant node, or to know when to put two spaces, four spaces, etc. I keep trying to find an integer value that indicates the level of descendancy of each node, but I am having no luck.

Can someone help?

Thanks!

Bill Norman
  • 883
  • 11
  • 29
  • Is the xml document a good example of the available nodes? Or could there be more or less nodes and descendance levels? – djv Mar 10 '17 at 21:42
  • There are hundreds of nodes in the file, but I am only selecting those nodes with a certain attribute. But those nodes have several levels of descendants, so the descendant level varies from sibling to sibling. I'm currently approaching it so there are nested 'for-each' loops to drill down to as many levels as I can, but it's difficult to know how far to drill and to stop drilling. In other words, how much nesting to do. Thanks for your interest. I hope I am explaining this properly. – Bill Norman Mar 10 '17 at 21:48
  • I'm not sure what technology you are using to read the xml. I use a lot of xml documents, but they always follow some standard model. I'll post an answer based on the model you provided. It may or may not work for you (but you could potentially generate more models based on your requirement). See my answer. – djv Mar 10 '17 at 21:50

1 Answers1

0

Do you have a vb.net class which matches your xml schema? If not, you can follow this post to generate one https://stackoverflow.com/a/17315863/832052.

The classes should look like this:

'''<remarks/>
<System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True), _
 System.Xml.Serialization.XmlRootAttribute([Namespace]:="", IsNullable:=False)> _
Partial Public Class Document

    Private pointRecordField() As DocumentPointRecord

    '''<remarks/>
    <System.Xml.Serialization.XmlElementAttribute("PointRecord")> _
    Public Property PointRecord() As DocumentPointRecord()
        Get
            Return Me.pointRecordField
        End Get
        Set(value As DocumentPointRecord())
            Me.pointRecordField = value
        End Set
    End Property
End Class

'''<remarks/>
<System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True)> _
Partial Public Class DocumentPointRecord

    Private nameField As String

    Private codeField As String

    Private methodField As String

    Private classificationField As String

    Private deletedField As Boolean

    Private eCEFDeltasField As DocumentPointRecordECEFDeltas

    Private idField As Byte

    Private timeStampField As Date

    '''<remarks/>
    Public Property Name() As String
        Get
            Return Me.nameField
        End Get
        Set(value As String)
            Me.nameField = value
        End Set
    End Property

    '''<remarks/>
    Public Property Code() As String
        Get
            Return Me.codeField
        End Get
        Set(value As String)
            Me.codeField = value
        End Set
    End Property

    '''<remarks/>
    Public Property Method() As String
        Get
            Return Me.methodField
        End Get
        Set(value As String)
            Me.methodField = value
        End Set
    End Property

    '''<remarks/>
    Public Property Classification() As String
        Get
            Return Me.classificationField
        End Get
        Set(value As String)
            Me.classificationField = value
        End Set
    End Property

    '''<remarks/>
    Public Property Deleted() As Boolean
        Get
            Return Me.deletedField
        End Get
        Set(value As Boolean)
            Me.deletedField = value
        End Set
    End Property

    '''<remarks/>
    Public Property ECEFDeltas() As DocumentPointRecordECEFDeltas
        Get
            Return Me.eCEFDeltasField
        End Get
        Set(value As DocumentPointRecordECEFDeltas)
            Me.eCEFDeltasField = value
        End Set
    End Property

    '''<remarks/>
    <System.Xml.Serialization.XmlAttributeAttribute()> _
    Public Property ID() As Byte
        Get
            Return Me.idField
        End Get
        Set(value As Byte)
            Me.idField = value
        End Set
    End Property

    '''<remarks/>
    <System.Xml.Serialization.XmlAttributeAttribute()> _
    Public Property TimeStamp() As Date
        Get
            Return Me.timeStampField
        End Get
        Set(value As Date)
            Me.timeStampField = value
        End Set
    End Property
End Class

'''<remarks/>
<System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True)> _
Partial Public Class DocumentPointRecordECEFDeltas

    Private deltaXField As DocumentPointRecordECEFDeltasDeltaX

    Private deltaYField As DocumentPointRecordECEFDeltasDeltaY

    Private deltaZField As DocumentPointRecordECEFDeltasDeltaZ

    '''<remarks/>
    Public Property DeltaX() As DocumentPointRecordECEFDeltasDeltaX
        Get
            Return Me.deltaXField
        End Get
        Set(value As DocumentPointRecordECEFDeltasDeltaX)
            Me.deltaXField = value
        End Set
    End Property

    '''<remarks/>
    Public Property DeltaY() As DocumentPointRecordECEFDeltasDeltaY
        Get
            Return Me.deltaYField
        End Get
        Set(value As DocumentPointRecordECEFDeltasDeltaY)
            Me.deltaYField = value
        End Set
    End Property

    '''<remarks/>
    Public Property DeltaZ() As DocumentPointRecordECEFDeltasDeltaZ
        Get
            Return Me.deltaZField
        End Get
        Set(value As DocumentPointRecordECEFDeltasDeltaZ)
            Me.deltaZField = value
        End Set
    End Property
End Class

'''<remarks/>
<System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True)> _
Partial Public Class DocumentPointRecordECEFDeltasDeltaX

    Private valueField As Decimal

    Private inToleranceField As String

    '''<remarks/>
    Public Property Value() As Decimal
        Get
            Return Me.valueField
        End Get
        Set(value As Decimal)
            Me.valueField = value
        End Set
    End Property

    '''<remarks/>
    Public Property InTolerance() As String
        Get
            Return Me.inToleranceField
        End Get
        Set(value As String)
            Me.inToleranceField = value
        End Set
    End Property
End Class

'''<remarks/>
<System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True)> _
Partial Public Class DocumentPointRecordECEFDeltasDeltaY

    Private valueField As Decimal

    Private inToleranceField As String

    '''<remarks/>
    Public Property Value() As Decimal
        Get
            Return Me.valueField
        End Get
        Set(value As Decimal)
            Me.valueField = value
        End Set
    End Property

    '''<remarks/>
    Public Property InTolerance() As String
        Get
            Return Me.inToleranceField
        End Get
        Set(value As String)
            Me.inToleranceField = value
        End Set
    End Property
End Class

'''<remarks/>
<System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True)> _
Partial Public Class DocumentPointRecordECEFDeltasDeltaZ

    Private valueField As Decimal

    Private inToleranceField As String

    '''<remarks/>
    Public Property Value() As Decimal
        Get
            Return Me.valueField
        End Get
        Set(value As Decimal)
            Me.valueField = value
        End Set
    End Property

    '''<remarks/>
    Public Property InTolerance() As String
        Get
            Return Me.inToleranceField
        End Get
        Set(value As String)
            Me.inToleranceField = value
        End Set
    End Property
End Class

Now that you have a class in vb.net, you can serialize and deserialize your xml document to an instance of the class.

Imports System.Xml
Imports System.Xml.Serialization
Imports System.IO

Sub Main()

    Dim s = New XmlSerializer(GetType(Document))
    Dim d As Document
    ' use your filename here
    Using sr = New StreamReader("document.xml")
        d = CType(s.Deserialize(sr), Document)
    End Using
    For Each pr In d.PointRecord
        Console.WriteLine("Point Record ID: {0} Time Stamp: {1}",
                          pr.ID, pr.TimeStamp)
        Console.WriteLine("  Name: {0}",
                          pr.Name)
        Console.WriteLine("  Code: {0}",
                          pr.Code)
        Console.WriteLine("  Method: {0}",
                          pr.Method)
        Console.WriteLine("  Classication: {0}",
                          pr.Classification)
        Console.WriteLine("  Deleted: {0}",
                          pr.Deleted)
        Console.WriteLine("  ECFDeltas-")
        Console.WriteLine("    DeltaX: {0:0.000000000} In Tolerance: {1}",
                          pr.ECEFDeltas.DeltaX.Value, pr.ECEFDeltas.DeltaX.InTolerance)
        Console.WriteLine("    DeltaY: {0:0.000000000} In Tolerance: {1}",
                          pr.ECEFDeltas.DeltaY.Value, pr.ECEFDeltas.DeltaY.InTolerance)
        Console.WriteLine("    DeltaZ: {0:0.000000000} In Tolerance: {1}",
                          pr.ECEFDeltas.DeltaZ.Value, pr.ECEFDeltas.DeltaZ.InTolerance)
    Next
    Console.ReadLine()
End Sub

I added another <PointRecord ID="00000050" TimeStamp="2017-03-03T09:39:54"> node to generate the model with an array of PointRecord, assuming you have this.

<Document>
    <PointRecord ID="00000050" TimeStamp="2017-03-03T09:39:54">
        <Name>WF2510</Name>
        <Code>EOC RECT 6/</Code>
        <Method>StaticObservation</Method>
        <Classification>Normal</Classification>
        <Deleted>false</Deleted>

        <ECEFDeltas>
            <DeltaX>
              <Value>-14179.040909261</Value>
              <InTolerance>True</InTolerance>
            </DeltaX>
            <DeltaY>
              <Value>-3572.6636230592</Value>
              <InTolerance>True</InTolerance>
            </DeltaY>
            <DeltaZ>
              <Value>-8319.8607607852</Value>
              <InTolerance>False</InTolerance>
            </DeltaZ>
          </ECEFDeltas>
    </PointRecord>
    <PointRecord ID="00000051" TimeStamp="2017-03-03T09:39:54">
        <Name>WF2510</Name>
        <Code>EOC RECT 6/</Code>
        <Method>StaticObservation</Method>
        <Classification>Normal</Classification>
        <Deleted>false</Deleted>

        <ECEFDeltas>
            <DeltaX>
              <Value>-14179.040909261</Value>
              <InTolerance>True</InTolerance>
            </DeltaX>
            <DeltaY>
              <Value>-3572.6636230592</Value>
              <InTolerance>True</InTolerance>
            </DeltaY>
            <DeltaZ>
              <Value>-8319.8607607852</Value>
              <InTolerance>False</InTolerance>
            </DeltaZ>
          </ECEFDeltas>
    </PointRecord>
</Document>

When testing, my xml doc had two PointRecords. The output of the program:

Point Record ID: 50 Time Stamp: 3/3/2017 9:39:54 AM
  Name: WF2510
  Code: EOC RECT 6/
  Method: StaticObservation
  Classication: Normal
  Deleted: False
  ECFDeltas-
    DeltaX: -14179.040909261 In Tolerance: True
    DeltaY: -3572.663623059 In Tolerance: True
    DeltaZ: -8319.860760785 In Tolerance: False
Point Record ID: 51 Time Stamp: 3/3/2017 9:39:54 AM
  Name: WF2510
  Code: EOC RECT 6/
  Method: StaticObservation
  Classication: Normal
  Deleted: False
  ECFDeltas-
    DeltaX: -14179.040909261 In Tolerance: True
    DeltaY: -3572.663623059 In Tolerance: True
    DeltaZ: -8319.860760785 In Tolerance: False
Community
  • 1
  • 1
djv
  • 15,168
  • 7
  • 48
  • 72
  • You used serialization, which is a new concept for me. Your code does exactly what I need, so I am going to study that technique mode closely. Thanks a bunch! – Bill Norman Mar 10 '17 at 21:56
  • Uh oh. May be a problem. I didn't mention that this is a Windows application, not a web application. I'm getting errors because it doesn't recognize "document." – Bill Norman Mar 10 '17 at 22:04
  • Mine is a Windows console application. "document" is the name of my xml file, I put it alongside my code, in the solution, so it's copied to the output directory upon build. You should replace "document.xml" with the path to your xml document. – djv Mar 10 '17 at 22:15
  • Or did you mean on this line `Dim d As Document`? Then you must generate your classes from your xml. See the link in my first paragraph for details. You should have this class defined after doing that `Partial Public Class Document`... among others – djv Mar 10 '17 at 22:22
  • Yes, that was where it was showing the error. That's gone now, but got others going on. I'll get back to you shortly. – Bill Norman Mar 10 '17 at 22:28
  • I've got lots of errors saying, for example, 'Classification' is not a member of Project.FormName.DocumentPointRecord. But there is another issue that concerns me, and maybe I wasn't clear. The XML code will not always have the same elements, i,e, "Classification," "Deleted," etc. In other words, the structure beneath the PointRecord node will not be consistent from one record to the next. Which is why I need to extract the tag's name and its contents, using the Name as the label before the inner text field. Serialization is far above my current expertise, and it will take a while to absorb. – Bill Norman Mar 10 '17 at 22:42
  • When generating the class, you need to follow the method in the link. I'll describe it here: copy the entire xml document text. Go to visual studio >> Edit Menu >> Paste Special >> Paste XML as classes. It should generate all the classes corresponding to your xml document. There are online resources which can do this as well. I updated my answer with all the classes I generated from your example xml. – djv Mar 10 '17 at 22:50
  • I'm up against a weekend now, so I'll have to defer this until Monday. Thanks! – Bill Norman Mar 10 '17 at 23:02