Parse XML to get data for an Invoice

Hi everyone!

I have this VB.NET code for get data of an XML of an invoice:
Imports System.Xml

Private Function VarXml(ByRef xAtt As XmlElement, ByVal strVar As String) As String
    VarXml = xAtt.GetAttribute(strVar)
    If VarXml = Nothing Then VarXml = ""
End Function

Private Sub ExtraerVariables(ByVal strXML)
    Dim xDoc As XmlDocument
    Dim xNodo As XmlNodeList
    Dim xAtt As XmlElement
    Dim intConsecutivo As Integer
    xDoc = New XmlDocument
    xDoc.Load(strXML)
    xNodo = xDoc.GetElementsByTagName("cfdi:Comprobante")
    If xNodo.Count > 0 Then
        For Each xAtt In xNodo
            strTipoComprobante = VarXml(xAtt, "tipoDeComprobante")
            strSerie = VarXml(xAtt, "serie")
            strFolio = VarXml(xAtt, "folio")
            strFechaEmision = VarXml(xAtt, "fecha")
            strSello = VarXml(xAtt, "sello")
            strNoCertificado = VarXml(xAtt, "noCertificado")
            strSubtotal = VarXml(xAtt, "subTotal")
            strTotal = VarXml(xAtt, "total")
            strMoneda = VarXml(xAtt, "Moneda")
            strCondiciones = VarXml(xAtt, "condicionesDePago")
            strFormaPago = VarXml(xAtt, "formaDePago")
            strMetodoPago = (VarXml(xAtt, "metodoDePago") + " " + VarXml(xAtt, "NumCtaPago")).Trim
            strLugarExpedicion = VarXml(xAtt, "LugarExpedicion")
        Next
    End If
    xNodo = xDoc.GetElementsByTagName("cfdi:Emisor")
    If xNodo.Count > 0 Then
        For Each xAtt In xNodo
            strEmisorRfc = VarXml(xAtt, "rfc")
            strEmisorNombre = VarXml(xAtt, "nombre")
        Next
        For Each xAtt In xNodo.Item(0)
            If xAtt.LocalName Like "*DomicilioFiscal*" Then
                strEmisorCalle = VarXml(xAtt, "calle")
                strEmisorNoExterior = VarXml(xAtt, "noExterior")
                strEmisorNoInterior = VarXml(xAtt, "noInterior")
                strEmisorColonia = VarXml(xAtt, "colonia")
                strEmisorReferencia = VarXml(xAtt, "referencia")
                strEmisorMunicipio = VarXml(xAtt, "municipio")
                strEmisorEstado = VarXml(xAtt, "estado")
                strEmisorPais = VarXml(xAtt, "pais")
                strEmisorCodigoPostal = VarXml(xAtt, "codigoPostal")
            End If
            If xAtt.LocalName Like "*RegimenFiscal*" Then
                strRegimen = VarXml(xAtt, "Regimen")
            End If
        Next
    End If

End Sub

Now the question is,exist something similar in Xojo to parse a XML File to get the Values for make a printer version of an Electronic Invoice?.
Is there a Class or something?

Thanks in advance

You should search the language reference first

http://documentation.xojo.com/index.php/XMLDocument

[quote=190318:@Norman Palardy]You should search the language reference first

http://documentation.xojo.com/index.php/XMLDocument[/quote]
Thanks Norman, I’’ get a read

I dont recognise the code Gerardo pasted. Looks like some bizarre, ancient type of language designed to send programmers mad.

Yeah, I’ve read it and It has no sense.

This is the XML of the Invoice that I wan’t to get the data for use it on a Database:

<?xml version="1.0" encoding="utf-8"?>

I was reading this code, that reads the XML Root, the XML Child, the XMLGrandchild and the attributes:
'Cargando el XML

'Abrir un dialogo y establecer que xmlfile contiene el documento cargado
Dim xmlFile As FolderItem
xmlFile = GetOpenFolderItem(“”)

'Si la varibale xmlFile no esta vacia entonces
If xmlFile <> Nil Then
'Declarar la variable xml como un nuevo Documento XML
Dim xml As New XmlDocument

'Intentamos cargar la variable XMLFile que contiene el archivo leido
'a la variable xml que es el nuvo Documento que declaramos
Try
  xml.LoadXml(xmlFile)
  
  'Declaramos una Excepcion de XMl para detectar errores de apertura
Catch e As XmlException
  'Si hay tal error, mostrarlo en un MsgBox
  Msgbox("XML error: " + e.Message)
End Try

Dim xRoot As XmlNode
Dim xNode As XmlNode
Dim xChild As XmlNode
Dim xAtt As XmlAttribute

xRoot = xml.FirstChild
muestraxml.Text = xRoot.Name + EndOfLine

For i As Integer = 0 to xRoot.ChildCount - 1
  xNode = xRoot.Child(i)
  muestraxml.Text = muestraxml.Text + "Child Name : " + xNode.Name + EndOfLine 
  For j As Integer = 0 To xNode.ChildCount - 1
    xChild = xNode.Child(j)
    muestraxml.Text = muestraxml.Text + "Grandchild Name : " + xChild.Name + EndOfLine
    For k As Integer = 0 To xChild.AttributeCount - 1
      xAtt = xChild.GetAttributeNode(k)
      muestraxml.Text = muestraxml.Text + "Attribute Name : " + xAtt.Name + " Value : " + xAtt.Value + EndOfLine
    Next k
  Next j
Next i

muestraxml.Text = muestraxml.Text + "Done"

End If

Now I’m Trying to get the data, for example:
in GrandChild Name: cfdi:DomicilioFiscal get the title of this and load it to a variable, for use it as a header of a recordset.
and get a variable of each Attribute name for this GrandChild.

in GrandChild Name: cfdi:RegimenFiscal get the title of this and load it to a variable, for use it as a header of a recordset.
and get a variable of each Attribute name for this GrandChild.

and so on.

Any Ideas?

I get This:
'Abrir un dialogo y establecer que xmlfile contiene el documento cargado
Dim xmlFile As FolderItem
xmlFile = GetOpenFolderItem("")

'Si la varibale xmlFile no esta vacia entonces
If xmlFile <> Nil Then
'Declarar la variable xml como un nuevo Documento XML
Dim xml As New XmlDocument

'Intentamos cargar la variable XMLFile que contiene el archivo leido
'a la variable xml que es el nuvo Documento que declaramos
Try
  xml.LoadXml(xmlFile)
  
  'Declaramos una Excepcion de XMl para detectar errores de apertura
Catch e As XmlException
  'Si hay tal error, mostrarlo en un MsgBox
  Msgbox("XML error: " + e.Message)
End Try


Dim nodes As  XmlNodeList
nodes = xml.XQL ("//cfdi:DomicilioFiscal") //Find all cfdi:DomicilioFiscal in XML

//Loop through results and display each name attribute

Dim node as XmlNode
For i As Integer = 0 To nodes.Length-1
  node=nodes.Item(i)
  MsgBox("Domicilio Fiscal: " + node.GetAttribute("estado"))
Next

end if

With This XMLNodelist I can define what Kind of Info is that i Want and with get Attribute, i can set a value to search

When I’ want to get the result of a search in the XML, Example:

Instead of:
MsgBox("Domicilio Fiscal: " + node.GetAttribute(“estado”))

In order to show it on a TextArea:

Dim estado As String
estado = node.GetAttribute(“estado”))

Textarea.text = estado

It got me Syntax Error :frowning:

LOL, what a Dumb!!, I write an extra “)”

Hi Gerardo

Have a look at https://dl.dropboxusercontent.com/u/18858366/XMLInvoiceDemo.xojo_binary_project. It is a quickly made solution to parsing your invoice into classes (I have created just the Domicilio Fiscal class).

HTH

Wayne

[quote=190477:@Wayne Golding]Hi Gerardo

Have a look at https://dl.dropboxusercontent.com/u/18858366/XMLInvoiceDemo.xojo_binary_project. It is a quickly made solution to parsing your invoice into classes (I have created just the Domicilio Fiscal class).

HTH

Wayne[/quote]
Thanks man, I gonna Probe it, cuz. I made this code, It works but only with an XML file, If i choose another one, gives me that error:

msg: XML parser error 4: not well-formed (invalid token)

This is the code that I made, with the error that I commented:

'Declaramos una matriz Datos_Emisor_dom_Fiscal
'para almacenar los datos del domicilio fiscal del Emisor
Dim Datos_Emisor_dom_Fiscal(12) as string
'Donde:
'0 = xmlns:xsi
'1 = xmlns:cfdi
'2 = xmlns:xml
'3 = calle
'4 = Numero Exterior
'5 = Localidad
'6 = Municipio
'7 = Estado
'8 = Pais
'9 = Codigo Postal
'10 = Colonia

'Declaramos una matrix Data_Emisor_expedidoEn
'para almacenar los datos de donde se expidi
'el CFDI por parte del emisor

Dim Data_Emisor_expedidoEn(11) as string
'Donde:
'0 = xmlns:cfdi
'1 = xmlns:xml
'2 = calle
'3 = Numero Exterior
'4 = Colonia
'5 = Municipio
'6 = Estado
'7 = Pais
'8 = Codigo Postal

Dim Data_Emisor_RegimenFiscal(4) as string
'Donde:
'0 = xmlns:cfdi
'1 = xmlns:xml
'2 = Regimen

Dim Data_Receptor_Domicilio(9) as string
'Donde:
'0 = xmlns:cfdi
'1 = xmlns:xml
'2 = Regimen

'Abrir un dialogo y establecer que xmlfile contiene el documento cargado
Dim xmlFile As FolderItem
xmlFile = GetOpenFolderItem("")

'Si la varibale xmlFile no esta vacia entonces
If xmlFile <> Nil Then
'Declarar la variable xml como un nuevo Documento XML
Dim xml As New XmlDocument

'Intentamos cargar la variable XMLFile que contiene el archivo leido
'a la variable xml que es el nuvo Documento que declaramos
Try
  xml.LoadXml(xmlFile)
  
  'Declaramos una Excepcion de XMl para detectar errores de apertura
Catch e As XmlException
  'Si hay tal error, mostrarlo en un MsgBox
  Msgbox("XML error: " + e.Message)
End Try


Dim nodos_Emi_Dom_Fis,  nodos_Emi_Expedido, nodos_Emi_RegimenFiscal, nodos_Receptor_domicilio As  XmlNodeList
nodos_Emi_Dom_Fis = xml.XQL ("//cfdi:DomicilioFiscal") //Find all cfdi:DomicilioFiscal in XML
nodos_Emi_Expedido = xml.XQL ("//cfdi:ExpedidoEn") //Find all cfdi:ExpedidoEn in XML
nodos_Emi_RegimenFiscal = xml.XQL ("//cfdi:RegimenFiscal") //Find all cfdi:RegimenFiscal in XML
nodos_Receptor_domicilio = xml.XQL ("//cfdi:Domicilio") //Find all cfdi:Domicilio in XML

//Loop through results and display each name attribute

Dim Emi_Domicilio_Fiscal, Emi_Expedido, Emi_RegimenFiscal,Recep_Domicilio as XmlNode
For i As Integer = 0 To nodos_Emi_Dom_Fis.Length-1
  Emi_Domicilio_Fiscal=nodos_Emi_Dom_Fis.Item(i)
  
  'Asignamos valores a la matriz con los datos de domicilio fiscal
  Datos_Emisor_dom_Fiscal(0) = Emi_Domicilio_Fiscal.GetAttribute("xmlns:xsi")
  Datos_Emisor_dom_Fiscal(1) = Emi_Domicilio_Fiscal.GetAttribute("xmlns:cfdi")
  Datos_Emisor_dom_Fiscal(2) = Emi_Domicilio_Fiscal.GetAttribute("xmlns:xml")
  Datos_Emisor_dom_Fiscal(3) = Emi_Domicilio_Fiscal.GetAttribute("calle")
  Datos_Emisor_dom_Fiscal(4) = Emi_Domicilio_Fiscal.GetAttribute("noExterior")
  Datos_Emisor_dom_Fiscal(5) = Emi_Domicilio_Fiscal.GetAttribute("localidad")
  Datos_Emisor_dom_Fiscal(6) = Emi_Domicilio_Fiscal.GetAttribute("municipio")
  Datos_Emisor_dom_Fiscal(7) = Emi_Domicilio_Fiscal.GetAttribute("estado")
  Datos_Emisor_dom_Fiscal(8) = Emi_Domicilio_Fiscal.GetAttribute("pais")
  Datos_Emisor_dom_Fiscal(9) = Emi_Domicilio_Fiscal.GetAttribute("codigoPostal")
  Datos_Emisor_dom_Fiscal(10) = Emi_Domicilio_Fiscal.GetAttribute("colonia")
  
Next

For i As Integer = 0 To nodos_Emi_Expedido.Length-1
  Emi_Expedido=nodos_Emi_Expedido.Item(i)
  
  'Asignamos valores a la matriz con los datos de domicilio fiscal
  Data_Emisor_expedidoEn(0)=Emi_Expedido.GetAttribute("xmlns:cfdi")
  Data_Emisor_expedidoEn(1)=Emi_Expedido.GetAttribute("xmlns:xml")
  Data_Emisor_expedidoEn(2)=Emi_Expedido.GetAttribute("calle")
  Data_Emisor_expedidoEn(3)=Emi_Expedido.GetAttribute("noExterior")
  Data_Emisor_expedidoEn(4)=Emi_Expedido.GetAttribute("colonia")
  Data_Emisor_expedidoEn(5)=Emi_Expedido.GetAttribute("municipio")
  Data_Emisor_expedidoEn(6)=Emi_Expedido.GetAttribute("estado")
  Data_Emisor_expedidoEn(7)=Emi_Expedido.GetAttribute("pais")
  Data_Emisor_expedidoEn(8)=Emi_Expedido.GetAttribute("codigoPostal")
  
Next

For i As Integer = 0 To nodos_Emi_RegimenFiscal.Length-1
  Emi_RegimenFiscal=nodos_Emi_RegimenFiscal.Item(i)
  
  'Asignamos valores a la matriz con los datos de domicilio fiscal
  Data_Emisor_RegimenFiscal(0)=Emi_Expedido.GetAttribute("xmlns:cfdi")
  Data_Emisor_RegimenFiscal(1)=Emi_Expedido.GetAttribute("xmlns:xml")
  Data_Emisor_RegimenFiscal(2)=Emi_Expedido.GetAttribute("regimen")
  
Next

For i As Integer = 0 To nodos_Receptor_domicilio.Length-1
  Recep_Domicilio=nodos_Receptor_domicilio.Item(i)
  
  'Asignamos valores a la matriz con los datos de domicilio fiscal
  Data_Receptor_Domicilio(0)=Recep_Domicilio.GetAttribute("xmlns:cfdi")
  Data_Receptor_Domicilio(1)=Recep_Domicilio.GetAttribute("xmlns:xml")
  Data_Receptor_Domicilio(2)=Recep_Domicilio.GetAttribute("calle")
  Data_Receptor_Domicilio(3)=Recep_Domicilio.GetAttribute("noExterior")
  Data_Receptor_Domicilio(4)=Recep_Domicilio.GetAttribute("colonia")
  Data_Receptor_Domicilio(5)=Recep_Domicilio.GetAttribute("municipio")
  Data_Receptor_Domicilio(6)=Recep_Domicilio.GetAttribute("estado")
  Data_Receptor_Domicilio(7)=Recep_Domicilio.GetAttribute("pais")
  Data_Receptor_Domicilio(8)=Recep_Domicilio.GetAttribute("codigoPostal")
  
  
Next


' Mostramos en el TextArea la matriz que creamos con los datos de la factura
' Con estra primera linea definimos el texto que queremos poner antes en el textarea
' sumando muestraxml.text + el nuevo texto
muestraxml.text = "Datos del emisor" + EndOfLine+ EndOfLine + "Domicilio Fiscal:" +EndOfLine
'Con Join, Ingresamos toda la matriz Datos Emisor Domiciilio fiscal
'y con EndOfLine, aadimos un retorno de carro.
muestraxml.text = muestraxml.text  + Join (Datos_Emisor_dom_Fiscal, EndOfLine) +EndOfLine 

'Expedido en:
muestraxml.Text = muestraxml.Text + "Expedido en:" + EndOfLine
'Ingresamos los datos de donde se expidi la factura
muestraxml.text = muestraxml.text + Join (Data_Emisor_expedidoEn, EndOfLine)
'Ingresamos los datos de Regimen Fiscal
muestraxml.Text = muestraxml.Text +  EndOfLine +  EndOfLine + "Regimen Fiscal:" +  EndOfLine
muestraxml.Text = muestraxml.Text +  Join (Data_Emisor_RegimenFiscal, EndOfLine)
muestraxml.text = muestraxml.Text + EndOfLine + EndOfLine + "Datos del Receptor: "
muestraxml.text = muestraxml.Text + EndOfLine + EndOfLine + "Domicilio:" + EndOfLine + Join (Data_Receptor_Domicilio, EndOfLine)

end if

[quote=190477:@Wayne Golding]Hi Gerardo

Have a look at https://dl.dropboxusercontent.com/u/18858366/XMLInvoiceDemo.xojo_binary_project. It is a quickly made solution to parsing your invoice into classes (I have created just the Domicilio Fiscal class).

HTH

Wayne[/quote]

Hi Wayne, it looks good!.

Now I have a doubt, As I understand I press the Obtener Factura button, It loads the XML, A new XML document is created,and the content of the new document is “f” that is loaded from “Load”.

Once loaded to the new document, It calls the “ProcessNode” function and the parameter to analyze is the First Child of the XML.

So. The ProcessNode make this:

analize the XMLnode that passed before (the FirstChild)
for this Uses a Select Case for select several cases, the first one is Domicilio Fiscal, and so the next like ExpedidoEn and RegimenFiscal

when The Name is “Domicilio Fiscal” then:
objComprobante.objEmisor.objDomicilioFiscal.FromXMLNode(x)

when it says this, we are calling the FromXMLNode and passes x as argument.
where x is the xml node.

So,Inside “ClassDomicilioFiscal” I found “AddtoDBTable” inside of I must write a method to insert the data in the table of the database.

Now, the final question is, How did I invoke this method?

I’m thinking to call the method inside the processNode, like this:

Select Case x.LocalName
Case “DomicilioFiscal”
objComprobante.objEmisor.objDomicilioFiscal.FromXMLNode(x)
objComprobante.objEmisor.objDomicilioFiscal.AddToDBTable
End Select

// Procesar recursivamente los hijos de este nodo.
For i As Integer = 0 to x.ChildCount - 1
ProcessNode(x.Child(i))
Next

Am I Wrong?
Thanks

Gerado you are correct, I wasn’t sure if you wanted to parse the entire document first then add records to the database or add them as you go.

If you don’t want to use the data anywhere else you could create the object on the fly

Select Case x.LocalName Case "DomicilioFiscal" Dim objDomicilioFiscalAs New classDomicilioFiscal objDomicilioFiscal.FromXMLNode(x) objDomicilioFiscal.AddToDBTable End Select

Which would allow you to remove the constructors from the other classes giving you a flat structure.

[quote=190709:@Wayne Golding]Gerado you are correct, I wasn’t sure if you wanted to parse the entire document first then add records to the database or add them as you go.

If you don’t want to use the data anywhere else you could create the object on the fly

Select Case x.LocalName Case "DomicilioFiscal" Dim objDomicilioFiscalAs New classDomicilioFiscal objDomicilioFiscal.FromXMLNode(x) objDomicilioFiscal.AddToDBTable End Select

Which would allow you to remove the constructors from the other classes giving you a flat structure.[/quote]
Thanks Wayne :stuck_out_tongue:

[quote=190709:@Wayne Golding]Gerado you are correct, I wasn’t sure if you wanted to parse the entire document first then add records to the database or add them as you go.

If you don’t want to use the data anywhere else you could create the object on the fly

Select Case x.LocalName Case "DomicilioFiscal" Dim objDomicilioFiscalAs New classDomicilioFiscal objDomicilioFiscal.FromXMLNode(x) objDomicilioFiscal.AddToDBTable End Select

Which would allow you to remove the constructors from the other classes giving you a flat structure.[/quote]
Hi Wayne.

I’m creating the another classes to parse the “ExpedidoEn” and “RegimenFiscal” that are inside of “Emisor”,like “Domicilio FIscal”
So I understand the code as follows:

  1. The first Class is ClassComprobante, in its constructor Method declares objEmisor = New classEmisor
  2. The second Class or sub-class is ClassEmisor, which declares objDomicilioFiscal = New classDomicilioFiscal
  3. The third Class in “ClassDomicilioFIscal” who has the properties of the Domicilio Fiscal.

I made another class called “ClassExpedidoEn”, and I declare the Constructor in ClassEmisor.
For the new class I made its properties, I mean the Values or attributes of this node, like: Calle, Colonia, Municipio, CodigoPostal.
And I made a FromXMLNode to assign values to the properties.

But I get no values,

In ProcessNodeI add another Case, the first One was “DomicilioFiscal”, the next one is “ExpedidoEn”
and I put this:
objComprobante.objEmisor.objExpedidoEn.FromXMLNode(x)

Am I wrong or I missing something?

Thanks in advance :smiley:

I retract myself, It works. I’m able to read the “DomicilioFiscal” and the “ExpedidoEn” attributes :smiley: :smiley: :smiley:

[quote=190709:@Wayne Golding]Gerado you are correct, I wasn’t sure if you wanted to parse the entire document first then add records to the database or add them as you go.

If you don’t want to use the data anywhere else you could create the object on the fly

Select Case x.LocalName Case "DomicilioFiscal" Dim objDomicilioFiscalAs New classDomicilioFiscal objDomicilioFiscal.FromXMLNode(x) objDomicilioFiscal.AddToDBTable End Select

Which would allow you to remove the constructors from the other classes giving you a flat structure.[/quote]
I’m Getting all the Data Successfully existent on the Grandchildren, but I have an Issue:

I Also I have data in the Children,

For Example The First Child, As I understand is: cfdi:Emisor
So, I have this attributes: Nombre and RFC

I thought that ProcessNode process the first child of the XML Document.

So I added a Case:

Case “nombre”
objComprobante.objEmisor.FromXMLNode(x)


In ClassEmisor I made this:

  1. A method: “FromXMLNode” which inside of it contains this: strRFC = x.GetAttribute(“nombre”)
  2. Two Properties: strEmisor as String, strRFC As String

Am I Wrong? Any Ideas?
Thanks :smiley:

I dont Understand which are interpreting as FirstChild, The Child or the GrandChild?

If Cfdi:Emisor is the first Child, So I also try to make a Select Case with Emisor, but nothing happens :frowning:

I get the Value “nombre” attribute of the first Child “cf.:Emisor” with this:

xDocument.DocumentElement.FirstChild.GetAttribute(“nombre”)

LOL, yeah I got it using class. I forgot to make visible the label that I’m probing the values before to make the module.