Imprimir página | Cerrar ventana

Extraer Datos de XML y Volcarlos a una Tabla

Impreso de: Foro de Access y VBA
Categoría: Access y VBA
Nombre del foro: Access y VBA
Descripción del foro: Foro de programacion en Access (Con código y sin código)
URL: http://www.mvp-access.com/foro/forum_posts.asp?TID=86437
Fecha de impresión: 26/Septiembre/2022 a las 14:19


Tema: Extraer Datos de XML y Volcarlos a una Tabla
Publicado por: RUGALB
Asunto: Extraer Datos de XML y Volcarlos a una Tabla
Fecha de publicación: 05/Agosto/2022 a las 20:52
Hola Compañeros, buenas tardes.

Necesito Extraer los datos de un fichero XML, con las etiquetas e información correspondiente, basandome en el ejemplo de Mihura que sugiere en este hilo: http://www.mvp-access.com/foro/topic86265_post519739.html intente trabajar en ello pero no mefunciona, quiza sea por la forma en que estan declaradas las etiquetas, en lugar de ser simplemente así; <etiqueta> el fichero que tengo las tiene así: <etiqueta:algo>.

No logro hacerlo, alguien me puede dar un poco de luz al respecto por favor.

Mi fichero es así:

El XML es así:

<?xml version="1.0" encoding="utf-8"?>
<cfdi:Comprobante
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www."
    LugarExpedicion="0000"
    MetodoPago="PUE"
    TipoDeComprobante="I"
    Total="123"
    Moneda="MXN"
    Certificado="xxx"
    SubTotal="00.00"
    CondicionesDePago="Contado"
    NoCertificado="000000"
    FormaPago="03"
    Sello="XXXXXx"
    Fecha="2022-01-14T10:45:18"
    Folio="13"
    Serie="MM"
    Version="3.3"
    xmlns:cfdi="http://www.....">
    <cfdi:Emisor Rfc="AAAAAA" Nombre="XXXXX" RegimenFiscal="621"></cfdi:Emisor><cfdi:Receptor Rfc="XXXXXX" Nombre="XXXXX" UsoCFDI="G03"></cfdi:Receptor>
    <cfdi:Conceptos>
        <cfdi:Concepto ClaveProdServ="43212108" Cantidad="2" ClaveUnidad="H87" Unidad="pieza" Descripcion="Impresora Térmica de Ticket 58mm" ValorUnitario="500.00" Importe="1000.00">
            <cfdi:Impuestos>
                <cfdi:Traslados><cfdi:Traslado Base="000.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="000.00"></cfdi:Traslado></cfdi:Traslados>
            </cfdi:Impuestos>
        </cfdi:Concepto>
        <cfdi:Concepto ClaveProdServ="43201830" Cantidad="1" ClaveUnidad="H87" Unidad="pieza" Descripcion="Disco de Estado Sólido SSD 120GB" ValorUnitario="650.00" Importe="650.00">
            <cfdi:Impuestos>
                <cfdi:Traslados><cfdi:Traslado Base="650" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="000.00"></cfdi:Traslado></cfdi:Traslados>
            </cfdi:Impuestos>
        </cfdi:Concepto>
        <cfdi:Concepto ClaveProdServ="81111809" Cantidad="1" ClaveUnidad="E48" Unidad="servicio" Descripcion="Instalación de Software a Equipo de Cómputo" ValorUnitario="100.00" Importe="100.00">
            <cfdi:Impuestos>
                <cfdi:Traslados><cfdi:Traslado Base="100.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="00.00"></cfdi:Traslado></cfdi:Traslados>
            </cfdi:Impuestos>
        </cfdi:Concepto>
    </cfdi:Conceptos>
    <cfdi:Impuestos TotalImpuestosTrasladados="0000.00">
        <cfdi:Traslados><cfdi:Traslado Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="000.36"></cfdi:Traslado></cfdi:Traslados>
    </cfdi:Impuestos>
    <cfdi:Complemento>
        <tfd:TimbreFiscalDigital
            xmlns:tfd="http://www....."
            xsi:schemaLocation="http://www.....xsd"
            Version="1.1"
            UUID="000000000"
            FechaTimbrado="2022-01-14T10:57:01"
            RfcProvCertif="AAAAAAAAAAA"
            SelloCFD="XXXXXX"
            NoCertificadoSAT="000000"
            SelloSAT="000"
        />
    </cfdi:Complemento>
</cfdi:Comprobante>

Muy agradecido desde ya por sus comentarios.




-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)



Respuestas:
Publicado por: emiliove
Fecha de publicación: 05/Agosto/2022 a las 23:04
Como te decía tienes que mirar cada atributo y meterlos a una tabla, lo haces revisando nodo por nodo y extrayendo la información.

Como dice el Sr. Mihura primero bajas el xml en un Documento Dom y después revisas cada atributo de cada nodo.

Necesitas todos los atributos o solo algunos, la factura tu la generaste, y quieres los datos que no tienes del timbrado o es de otro lado. Puede ser algo así:


Private Sub Command()
    Dim oXml    As MSXML2.DOMDocument60
    Dim oNodes  As IXMLDOMNodeList
    Dim oNode   As IXMLDOMNode
    Dim i       As Long
    
    Set oXml = New MSXML2.DOMDocument60
    
        With oXml
           '.Load "C:\SISAdmon\Sistema Administrativo\CFD\2116ROEJ5804226HATimbrado.xml"
    .Load "tu Ruta con el archivo como lo de arriba"
            Set oNodes = .SelectNodes("//*")
            'SelectSingleNode
            For Each oNode In oNodes
                Debug.Print oNode.nodeName
                For i = 0 To oNode.Attributes.length - 1
                    Debug.Print oNode.Attributes(i).nodeName; Tab(30); _
                                oNode.Attributes(i).NodeValue
                Next i
            Next oNode
            Set oNodes = Nothing
        End With
        
    Set oXml = Nothing
End Sub



Publicado por: RUGALB
Fecha de publicación: 06/Agosto/2022 a las 00:50
muchísimas gracias por contestar Emilio :)

El fichero XML se genera externamente ya timbrado, se requieren leer todos los datos para poder llevarlos a una tabla y trabajar con esa información.

Estoy revisando el código que amablemente colocaste.




-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: anaaais
Fecha de publicación: 10/Agosto/2022 a las 16:45
Hola compañero

Solo tienes que implementar el objeto de "Microsoft XML Parser" (Microsoft.XMLDOM es el objeto COM de Microsoft XML Parser) y despues:
1. Carga el XML desde una ruta especificada.
2. Seleccione la etiqueta del archivo XML mediante SelectNodes o SelectSingleNode.
3. SelectNodes: selecciona una lista de nodos que coincidan con el patrón Xpath.
4. Recorre todos los nodos y para cada nodo obtenga los nodos secundarios, y luego recorra los nodos secundarios e imprímalos.

Y así podrás realizar todos los procesos o tratamiento de datos que quieres.

  Un saludo


Editado para eliminar una cita innecesaria al primer mensaje completo


Publicado por: RUGALB
Fecha de publicación: 11/Agosto/2022 a las 17:05
Muchas Gracias Anaaais, El código que Emilio puso es bastante bueno, obtiene toda la informacion de un XML, lo que comentas acerca de SelectNodes o SelectSingleNode para obtener información de ciertos parametros es correcto, el problema al que me enfrentaba ciertamente es a volcar esta información en registros en una tabla (cada registro dependiendo cuantas veces existiera la etiqueta <Concepto>)

Estoy trabajando en ello, al terminar lo posteare aquí como la solución que elaboré para obtener lo que necesitaba :)




-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: Bacterio
Fecha de publicación: 16/Agosto/2022 a las 11:47
Cuando tienes espacios de nombres como es tu caso, tendrás que especificarlos asignando la propiedad "SelectionNamespaces". Y luego usarlos en la cadena de búsqueda XPATH. Por ejemplo:

Dim xmlDoc As New MSXML2.DOMDocument
Dim node As MSXML2.IXMLDOMNode
Dim ndl As MSXML2.IXMLDOMNodeLIst

xmlDoc.async = False
xmlDoc.validateOnParse = True
xmlDoc.Load "YourFile.xml"
If xmlDoc.parseError.errorCode <> 0 Then
    Err.Raise vbObjecterror, , xmlDoc.parseError.reason
End If

xmlDoc.setProperty "SelectionNamespaces", "xmlns:cfdi='http://www.....' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "

Set node = xmlDoc.selectSingleNode("/cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto[@ClaveProdServ='43201830']")
' ...

Set ndl = xmlDoc.selectNodes("/cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto")
For Each node In ndl
    ' Aquí añadimos un nuevo registro a la tabla
    Debug.Print node.SelectSingleNode("@ClaveProdServ").nodeTypedValue
Next


Publicado por: xavi
Fecha de publicación: 16/Agosto/2022 a las 11:55
Bacterio!

Dichosos los ojos....


-------------
Xavi, un minyó de Terrassa

http://www.llodax.com" rel="nofollow - Mi web


Publicado por: Bacterio
Fecha de publicación: 16/Agosto/2022 a las 12:01
Hug

Pasaba por aquí y me atrajo el mensaje 


Publicado por: emiliove
Fecha de publicación: 16/Agosto/2022 a las 15:00
Ya que estas y para ti es mejor extraerlo con SelectSingleNodo o con Attributes.getNamedItem con Attributes.getNamedItem no necesitamos poner el espacio de nombre.


Saludos.


Publicado por: RUGALB
Fecha de publicación: 16/Agosto/2022 a las 17:43
Wow! Tanto Maestro aquí reunido

Como siempre me complico todo, lo he solucionado así y me funciona, se de antemano que tome el camino más dificilDisapprove

Sub listatodoxml2()
    
    Dim oXml    As Object 'MSXML2.DOMDocument60
    Dim oNodes  As IXMLDOMNodeList
    Dim oNode   As IXMLDOMNode
    Dim i       As Long
    
    Dim xNodoComprobante 'Para tomar los datos de la Etiqueta Comprobante
    Dim xListaNodoComprobante As Object ' nodos

    Dim xNodoEmisor 'Para tomar los datos de la Etiqueta Emisor
    Dim xListaNodoEmisor As Object ' nodos
    
    Dim xNodoReceptor 'Para tomar los datos de la Etiqueta Emisor
    Dim xListaNodoReceptor As Object ' nodos

    Dim xNodoImpuestosConcepto 'Para Tomar los Datos de la Etiqueta Impuestos por partida
    Dim xListaNodoImpuestosConcepto As Object ' nodos

    Dim xNodoImpuestos 'Para Tomar los Datos de la Etiqueta Impuestos Global Factura
    Dim xListaNodoImpuestos As Object ' nodos

    Dim xNodoComplementoTFD 'Para Tomar los Datos de la Etiqueta Impuestos
    Dim xListaNodoComplementoTFD As Object ' nodos
    
    Dim mydb As DAO.Database
    Dim rstx As DAO.Recordset
    Dim sqlborratbl As String
    Dim Count As Integer  'Cuenta Conceptos
    Dim CountImp As Integer  'Cuenta Impuestos de Conceptos
 
sqlborratbl = "delete * from [tblXML]"
CurrentDb.Execute sqlborratbl
    
    Set oXml = CreateObject("MSXML2.DOMDocument")  'New MSXML2.DOMDocument60
    
       With oXml
        .Load "C:\........\ejemplocfdi.xml"
            Set oNodes = .getElementsByTagName("cfdi:Concepto")
                 Count = 0
            'SelectSingleNode
            For Each oNode In oNodes
                'Debug.Print oNode.nodeName
                Count = Count + 1
                                    'Debug.Print "#### COMIENZA REGISTRO####"
                                        
                For i = 0 To oNode.Attributes.Length - 1
                   Debug.Print oNode.Attributes(i).nodeName; Tab(30); _
                                oNode.Attributes(i).nodeValue
                       Next i
                       
                      Set xListaNodoComprobante = oXml.selectNodes("/cfdi:Comprobante")
                          For Each xNodoComprobante In xListaNodoComprobante
                      
                      Set xListaNodoEmisor = oXml.selectNodes("/cfdi:Comprobante/cfdi:Emisor")
                          For Each xNodoEmisor In xListaNodoEmisor
                       
                      Set xListaNodoReceptor = oXml.selectNodes("/cfdi:Comprobante/cfdi:Receptor")
                          For Each xNodoReceptor In xListaNodoReceptor
                       
                      Set xListaNodoImpuestos = oXml.selectNodes("/cfdi:Comprobante/cfdi:Impuestos/cfdi:Traslados/cfdi:Traslado")
                           For Each xNodoImpuestos In xListaNodoImpuestos
                      
                      Set xListaNodoComplementoTFD = oXml.selectNodes("/cfdi:Comprobante/cfdi:Complemento/tfd:TimbreFiscalDigital")
                            For Each xNodoComplementoTFD In xListaNodoComplementoTFD
   
                         Set mydb = CurrentDb
                         Set rstx = mydb.OpenRecordset("tblXML")
                     
                          rstx.AddNew
                            rstx!Id = Count
                            rstx!Concepto_ClaveProdServ = oNode.Attributes.getNamedItem("ClaveProdServ").text
                            rstx!Concepto_Cantidad = oNode.Attributes.getNamedItem("Cantidad").text
                            rstx!Concepto_ClaveUnidad = oNode.Attributes.getNamedItem("ClaveUnidad").text
                            rstx!Concepto_Unidad = oNode.Attributes.getNamedItem("Unidad").text
                            rstx!Concepto_Descripcion = oNode.Attributes.getNamedItem("Descripcion").text
                            rstx!Concepto_ValorUnitario = oNode.Attributes.getNamedItem("ValorUnitario").text
                            rstx!Concepto_Importe = oNode.Attributes.getNamedItem("Importe").text
                            rstx!Comprobante_xmlns_xsi = xNodoComprobante.Attributes.getNamedItem("xmlns:xsi").text
                            rstx!Comprobante_xsi_schemaLocation = xNodoComprobante.Attributes.getNamedItem("xsi:schemaLocation").text
                            rstx!Comprobante_LugarExpedicion = xNodoComprobante.Attributes.getNamedItem("LugarExpedicion").text
                            rstx!Comprobante_MetodoPago = xNodoComprobante.Attributes.getNamedItem("MetodoPago").text
                            rstx!Comprobante_TipoDeComprobante = xNodoComprobante.Attributes.getNamedItem("TipoDeComprobante").text
                            rstx!Comprobante_Total = xNodoComprobante.Attributes.getNamedItem("Total").text
                            rstx!Comprobante_Moneda = xNodoComprobante.Attributes.getNamedItem("Moneda").text
                            rstx!Comprobante_Certificado = xNodoComprobante.Attributes.getNamedItem("Certificado").text
                            rstx!Comprobante_SubTotal = xNodoComprobante.Attributes.getNamedItem("SubTotal").text
                            rstx!Comprobante_CondicionesDePago = xNodoComprobante.Attributes.getNamedItem("CondicionesDePago").text
                            rstx!Comprobante_NoCertificado = xNodoComprobante.Attributes.getNamedItem("NoCertificado").text
                            rstx!Comprobante_FormaPago = xNodoComprobante.Attributes.getNamedItem("FormaPago").text
                            rstx!Comprobante_Sello = xNodoComprobante.Attributes.getNamedItem("Sello").text
                            rstx!Comprobante_Fecha = xNodoComprobante.Attributes.getNamedItem("Fecha").text
                            rstx!Comprobante_Folio = xNodoComprobante.Attributes.getNamedItem("Folio").text
                            rstx!Comprobante_Serie = xNodoComprobante.Attributes.getNamedItem("Serie").text
                            rstx!Comprobante_Version = xNodoComprobante.Attributes.getNamedItem("Version").text
                            rstx!Comprobante_xmlns_cfdi = xNodoComprobante.Attributes.getNamedItem("xmlns:cfdi").text
                            rstx!Emisor_Rfc = xNodoEmisor.Attributes.getNamedItem("Rfc").text
                            rstx!Emisor_Nombre = xNodoEmisor.Attributes.getNamedItem("Nombre").text
                            rstx!Emisor_RegimenFiscal = xNodoEmisor.Attributes.getNamedItem("RegimenFiscal").text
                            rstx!Receptor_Rfc = xNodoReceptor.Attributes.getNamedItem("Rfc").text
                            rstx!Receptor_Nombre = xNodoReceptor.Attributes.getNamedItem("Nombre").text
                            rstx!Receptor_UsoCFDI = xNodoReceptor.Attributes.getNamedItem("UsoCFDI").text
                            rstx!Impuestos_Importe = xNodoImpuestos.Attributes.getNamedItem("Importe").text
                            rstx!Impuestos_TasaOCuota = xNodoImpuestos.Attributes.getNamedItem("TasaOCuota").text
                            rstx!Impuestos_TipoFactor = xNodoImpuestos.Attributes.getNamedItem("TipoFactor").text
                            rstx!Impuestos_Impuesto = xNodoImpuestos.Attributes.getNamedItem("Impuesto").text
                            rstx!Complemento_xmlns_tfd = xNodoComplementoTFD.Attributes.getNamedItem("xmlns:tfd").text
                            rstx!Complemento_xsi_schemaLocation = xNodoComplementoTFD.Attributes.getNamedItem("xsi:schemaLocation").text
                            rstx!Complemento_Version = xNodoComplementoTFD.Attributes.getNamedItem("Version").text
                            rstx!Complemento_UUID = xNodoComplementoTFD.Attributes.getNamedItem("UUID").text
                            rstx!Complemento_FechaTimbrado = xNodoComplementoTFD.Attributes.getNamedItem("FechaTimbrado").text
                            rstx!Complemento_RfcProvCertif = xNodoComplementoTFD.Attributes.getNamedItem("RfcProvCertif").text
                            rstx!Complemento_SelloCFD = xNodoComplementoTFD.Attributes.getNamedItem("SelloCFD").text
                            rstx!Complemento_NoCertificadoSAT = xNodoComplementoTFD.Attributes.getNamedItem("NoCertificadoSAT").text
                            rstx!Complemento_SelloSAT = xNodoComplementoTFD.Attributes.getNamedItem("SelloSAT").text
                         rstx.Update
                         
                       Next xNodoComplementoTFD
                     Next xNodoImpuestos
                   Next xNodoReceptor
                 Next xNodoEmisor
               Next xNodoComprobante
             Next oNode

                                                                                                                           'Solo funciona con MSXML2.DOMDocument
               Set xListaNodoImpuestosConcepto = oXml.selectNodes("/cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto/cfdi:Impuestos/cfdi:Traslados/cfdi:Traslado")
               
                CountImp = 0
               For Each xNodoImpuestosConcepto In xListaNodoImpuestosConcepto
                CountImp = CountImp + 1
                'Debug.Print CountImp & " - " & xNodoImpuestosConcepto.Attributes.getNamedItem("Importe").text
                
                DoCmd.SetWarnings False ' Actualizo los datos faltantes de impuestos x concepto
                DoCmd.RunSQL "UPDATE tblXML SET tblXML.Concepto_Impuesto_Base = '" & xNodoImpuestosConcepto.Attributes.getNamedItem("Base").text & "' , tblXML.Concepto_Impuesto_Impuesto = '" & xNodoImpuestosConcepto.Attributes.getNamedItem("Impuesto").text & "' , tblXML.Concepto_Impuesto_TipoFactor = '" & xNodoImpuestosConcepto.Attributes.getNamedItem("TipoFactor").text & "' , tblXML.Concepto_Impuesto_TasaOCuota = '" & xNodoImpuestosConcepto.Attributes.getNamedItem("TasaOCuota").text & "',tblXML.Concepto_Impuesto_Importe = " & xNodoImpuestosConcepto.Attributes.getNamedItem("Importe").text & " where id=" & CountImp & ""
                DoCmd.SetWarnings True
                
               Next xNodoImpuestosConcepto
            
           
            Set oNodes = Nothing
        End With
    Set oXml = Nothing
  End Sub

Me llama la atención el Código que me hizo favor de colocar Bacterio para trabajar con los namespacesShocked

Lo voy a tratar de implementar y les cuento como me fue.

Excelente día para todos


-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: RUGALB
Fecha de publicación: 16/Agosto/2022 a las 18:09
Ya me puse a trabajar con el Código que amablemente proporciono Bacterio, aquí es donde me surge la duda...

Cada Nodo <cfdi:Concepto> tiene subnodos:
 <cfdi:Conceptos>
        <cfdi:Concepto ClaveProdServ="43212108" Cantidad="2" ClaveUnidad="H87" Unidad="pieza" Descripcion="Impresora Térmica de Ticket 58mm" ValorUnitario="500.00" Importe="1000.00">
            <cfdi:Impuestos>
                <cfdi:Traslados><cfdi:Traslado Base="000.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="000.00"></cfdi:Traslado></cfdi:Traslados>
            </cfdi:Impuestos>
        </cfdi:Concepto>
        <cfdi:Concepto ClaveProdServ="43201830" Cantidad="1" ClaveUnidad="H87" Unidad="pieza" Descripcion="Disco de Estado Sólido SSD 120GB" ValorUnitario="650.00" Importe="650.00">
            <cfdi:Impuestos>
                <cfdi:Traslados><cfdi:Traslado Base="650" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="000.00"></cfdi:Traslado></cfdi:Traslados>
            </cfdi:Impuestos>
        </cfdi:Concepto>
        <cfdi:Concepto ClaveProdServ="81111809" Cantidad="1" ClaveUnidad="E48" Unidad="servicio" Descripcion="Instalación de Software a Equipo de Cómputo" ValorUnitario="100.00" Importe="100.00">
            <cfdi:Impuestos>
                <cfdi:Traslados><cfdi:Traslado Base="100.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="00.00"></cfdi:Traslado></cfdi:Traslados>
            </cfdi:Impuestos>
        </cfdi:Concepto>
    </cfdi:Conceptos>

¿Como puedo Anidar esos valores en una sola linea para irlos pasando como registros a una tabla?

y que obtenga esto:


Gracias por su apoyo.


-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: Bacterio
Fecha de publicación: 17/Agosto/2022 a las 22:43
Creo que te has complicado muchísimo. Te paso un ejemplo más estructurado

Public Function XmlTest1(Optional ByRef strMensajeError) As Boolean
    Dim bResult As Boolean
    Dim xmlDoc As MSXML2.DOMDocument60
    Dim ndComprobante As MSXML2.IXMLDOMNode
    Dim ndConcepto As MSXML2.IXMLDOMNode
    Dim ndConceptoTraslado As MSXML2.IXMLDOMNode
    Dim ndEmisor As MSXML2.IXMLDOMNode
    Dim ndReceptor As MSXML2.IXMLDOMNode
    Dim ndImpuestos As MSXML2.IXMLDOMNode
    Dim ndImpuestosTraslado As MSXML2.IXMLDOMNode
    Dim ndComplemento As MSXML2.IXMLDOMNode
    Dim ndComplemento_TimbreFiscalDigital As MSXML2.IXMLDOMNode
    
    Dim att As MSXML2.IXMLDOMAttribute
    On Error GoTo HandleErr

    bResult = False

    Set xmlDoc = New MSXML2.DOMDocument60
    xmlDoc.async = False
    xmlDoc.validateOnParse = True
    xmlDoc.Load "C:\temp\xml-foro.xml"
    If xmlDoc.parseError.errorCode <> 0 Then
        Err.Raise vbObjectError, , xmlDoc.parseError.reason
    End If

    xmlDoc.SetProperty "SelectionNamespaces", "xmlns:cfdi='http://www.....' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "

    Set ndComprobante = xmlDoc.selectSingleNode("/cfdi:Comprobante") 'También valdría   xmlDoc.documentElement
        
    Dim dFechaHoraComprobante As Date
    dFechaHoraComprobante = FechaHoraFromXml(ndComprobante.Attributes.getNamedItem("Fecha").nodeTypedValue)
    Debug.Print "Procesando comprobante con fecha y hora " & dFechaHoraComprobante
    
    Set ndEmisor = ndComprobante.selectSingleNode("cfdi:Emisor")
    Set ndReceptor = ndComprobante.selectSingleNode("cfdi:Receptor")
    Set ndImpuestos = ndComprobante.selectSingleNode("cfdi:Impuestos")
    Set ndImpuestosTraslado = ndImpuestos.selectSingleNode("cfdi:Traslados/cfdi:Traslado")
    Set ndComplemento = ndComprobante.selectSingleNode("cfdi:Complemento")
    Set ndComplemento_TimbreFiscalDigital = ndComplemento.selectSingleNode("cfdi:TimbreFiscalDigital")
    
    For Each att In ndComprobante.Attributes
        Debug.Print ndComprobante.baseName & "_" & AttributeNameToFieldName(att.Name), att.Value
    Next
    For Each att In ndEmisor.Attributes
        Debug.Print ndEmisor.baseName & "_" & AttributeNameToFieldName(att.Name), att.Value
    Next
    For Each att In ndReceptor.Attributes
        Debug.Print ndReceptor.baseName & "_" & AttributeNameToFieldName(att.Name), att.Value
    Next
    For Each att In ndImpuestos.Attributes
        Debug.Print ndImpuestos.baseName & "_" & AttributeNameToFieldName(att.Name), att.Value
    Next
    For Each att In ndImpuestosTraslado.Attributes
        Debug.Print ndImpuestos.baseName & "_" & ndImpuestosTraslado.baseName & "_" & AttributeNameToFieldName(att.Name), att.Value
    Next
    For Each att In ndComplemento_TimbreFiscalDigital.Attributes
        Debug.Print ndComplemento.baseName & "_" & AttributeNameToFieldName(att.Name), att.Value
    Next
    
    For Each ndConcepto In ndComprobante.selectNodes("cfdi:Conceptos/cfdi:Concepto")
        Dim strClaveProdServ As String, strDescripcionConcepto As String
        strClaveProdServ = ndConcepto.selectSingleNode("@ClaveProdServ").nodeTypedValue
        strDescripcionConcepto = ndConcepto.selectSingleNode("@Descripcion").nodeTypedValue
        Debug.Print
        Debug.Print "Procesando concepto " & strClaveProdServ & " " & strDescripcionConcepto
               
        For Each att In ndConcepto.Attributes
            Debug.Print , ndConcepto.baseName & "_" & AttributeNameToFieldName(att.Name), att.Value
        Next
        Set ndConceptoTraslado = ndConcepto.selectSingleNode("cfdi:Impuestos/cfdi:Traslados/cfdi:Traslado")
        For Each att In ndConceptoTraslado.Attributes
            Debug.Print , ndConcepto.baseName & "_" & ndConceptoTraslado.baseName & "_" & AttributeNameToFieldName(att.Name), att.Value
        Next
        
        Dim cBaseTraslado As Currency, dblTasa As Double
        cBaseTraslado = CurrencyFromXml(ndConceptoTraslado.selectSingleNode("@Base").nodeTypedValue)
        dblTasa = DoubleFromXml(ndConceptoTraslado.selectSingleNode("@TasaOCuota").nodeTypedValue)
        
    Next

    bResult = True

ExitHere:
    XmlTest1 = bResult
    On Error Resume Next
    Exit Function
HandleErr:
    If IsMissing(strMensajeError) Then
        MsgBox Err.Description, vbCritical
    Else
        strMensajeError = Err.Description
    End If
    bResult = False
    Resume ExitHere
    Resume
End Function

Private Function AttributeNameToFieldName(ByVal strEntrada As String) As String
    AttributeNameToFieldName = Replace(strEntrada, ":", "_")
End Function

Private Function FechaHoraFromXml(ByVal strEntrada As String) As Date
    FechaHoraFromXml = CDate(Replace(strEntrada, "T", " "))
End Function

Private Function CurrencyFromXml(ByVal strEntrada As String) As Currency
    Dim cResult As Currency
    
    cResult = 0
    If Trim(strEntrada) = "" Then
        'Nada
    ElseIf Not IsNumeric(strEntrada) Then
        'Nada
    Else
        cResult = CCur(Replace(strEntrada, ".", ","))
    End If
    CurrencyFromXml = cResult
End Function

Private Function DoubleFromXml(ByVal strEntrada As String) As Currency
    Dim dblResult As Double
    
    dblResult = 0
    If Trim(strEntrada) = "" Then
        'Nada
    ElseIf Not IsNumeric(strEntrada) Then
        'Nada
    Else
        dblResult = CDbl(Replace(strEntrada, ".", ","))
    End If
    DoubleFromXml = dblResult
End Function

A ver por dónde empiezo ...

1.- No uses el método "selectNodes" para acceder a un nodo que no se repite. Utiliza el método "selectSingleNode". Hablo de 

Set ndComprobante = xmlDoc.selectSingleNode("/cfdi:Comprobante") 'También valdría   xmlDoc.documentElement

, en lugar de 

Set xListaNodoComprobante = oXml.selectNodes("/cfdi:Comprobante")
For Each xNodoComprobante In xListaNodoComprobante
' ...
Next
Lo único que consigues es hacer el código más voluminoso, complicado e ineficiente con bucles anidados.

2.- Los métodos "selectNodes" y "selectSingleNode" también están disponibles para los nodos. Aprovecha la estructura jerárquica de XML. 

Cuando se ejecuta una línea como
Set ndEmisor = ndComprobante.selectSingleNode("cfdi:Emisor")
, la búsqueda se hace en el fragmento xml del nodo <cfdi:Comprobante>

Si en lugar de la línea anterior ejecutas
Set xListaNodoEmisor = oXml.selectSingleNode("/cfdi:Comprobante/cfdi:Emisor")
, la búsqueda se hace en todo el XML. Eso lo hace mucho más ineficiente y lento. Además de que puede dar lugar a resultados inesperados.

3.- De nuevo llamo tu atención XML sobre la estructura jerárquica de XML. 
En la medida de lo posible, procesa el xml siguiendo la estructuración del mismo. Me refiero por ejemplo a acceso al nodo Emisor dentro del bucle principal en el que procesas conceptos. En lugar de 

Set xListaNodoEmisor = oXml.selectNodes("/cfdi:Comprobante/cfdi:Emisor")

, sería más sencillo, rápido y cómodo hacerlo como en el nuevo ejemplo que he pasado. 

Set ndEmisor = ndComprobante.selectSingleNode("cfdi:Emisor")

En general, si un nodo está en un nivel jerárquico superior a otro, lo normal es que se procese antes: el nodo contenedor antes que el contenido

También sugiero hacer la asignación fuera del bucle de los <cfdi:conceptos>. ¿Para que asignar el mismo nodo una y otra vez en cada ciclo del bucle?

4.- Estructura de tabla(s)
La información de los atributos de los nodos comprobante, emisor, receptor, complemento, ... es la misma para todo el xml. En el diseño de tabla que pareces tener, tienes un registro para cada concepto. Y en cada uno de esos registros repites la información de esos atributos.

Si has decidido de forma consciente y deliberada "desnormalizar" no tengo ningún comentario que hacer. Pero si tu intención no era esa debo indicarte que necesitarás al menos dos tablas. Una para los datos del comprobante y los nodos que no se repiten (emisor, receptor, complemento, ...) y otra para los múltiples conceptos incluidos en el comprobante.

En el nuevo ejemplo muestro como acceder a los nodos "cfdi:Impuestos/cfdi:Traslados/cfdi:Traslado" al procesar un nodo "cfdi:Concepto" como preguntabas

Creo que has escrito el código antes de analizar lo suficiente la información proporcionada y meditar sobre cómo aplicarla a la tarea a realizar. No es una crítica porque desconozco el tiempo del que dispones para hacer que esto funcione. Todos tenemos fechas que cumplir y no siempre podemos dedicar el tiempo suficiente a investigar y aprender. 

Te sugiero que compares el código que publicaste con el nuevo ejemplo para ver las diferencias.

Ten presente que cada maestrillo tiene su librillo como decimos aquí. Lo que te he mostrado es la forma en que a mí me gusta tratar con los XML's.

Otra persona podría sugerir que simplifiques y sustituyas las cadenas "<cfdi:" por "<" y "</cfdi:" por "</". De ese modo podrías olvidarte de los espacios de nombre y el acceso a los nodos sería más sencillo.





Publicado por: Bacterio
Fecha de publicación: 17/Agosto/2022 a las 22:53
Esta sería la salida para el xml que pusiste de ejemplo en el mensaje inicial:

Procesando comprobante con fecha y hora 14/01/2022 10:45:18
Comprobante_xmlns_xsi       http://www.w3.org/2001/XMLSchema-instance
Comprobante_xsi_schemaLocation            http://www.
Comprobante_LugarExpedicion 0000
Comprobante_MetodoPago      PUE
Comprobante_TipoDeComprobante             I
Comprobante_Total           123
Comprobante_Moneda          MXN
Comprobante_Certificado     xxx
Comprobante_SubTotal        00.00
Comprobante_CondicionesDePago             Contado
Comprobante_NoCertificado   000000
Comprobante_FormaPago       03
Comprobante_Sello           XXXXXx
Comprobante_Fecha           2022-01-14T10:45:18
Comprobante_Folio           13
Comprobante_Serie           MM
Comprobante_Version         3.3
Comprobante_xmlns_cfdi      http://www.....
Emisor_Rfc    AAAAAA
Emisor_Nombre XXXXX
Emisor_RegimenFiscal        621
Receptor_Rfc  XXXXXX
Receptor_Nombre             XXXXX
Receptor_UsoCFDI            G03
Impuestos_TotalImpuestosTrasladados       0000.00
Impuestos_Traslado_Impuesto 002
Impuestos_Traslado_TipoFactor             Tasa
Impuestos_Traslado_TasaOCuota             0.160000
Impuestos_Traslado_Importe  000.36
Complemento_xmlns_tfd       http://www.....
Complemento_xsi_schemaLocation            http://www.....xsd
Complemento_Version         1.1
Complemento_UUID            000000000
Complemento_FechaTimbrado   2022-01-14T10:57:01
Complemento_RfcProvCertif   AAAAAAAAAAA
Complemento_SelloCFD        XXXXXX
Complemento_NoCertificadoSAT              000000
Complemento_SelloSAT        000

Procesando concepto 43212108 Impresora Térmica de Ticket 58mm
              Concepto_ClaveProdServ      43212108
              Concepto_Cantidad           2
              Concepto_ClaveUnidad        H87
              Concepto_Unidad             pieza
              Concepto_Descripcion        Impresora Térmica de Ticket 58mm
              Concepto_ValorUnitario      500.00
              Concepto_Importe            1000.00
              Concepto_Traslado_Base      000.00
              Concepto_Traslado_Impuesto  002
              Concepto_Traslado_TipoFactor              Tasa
              Concepto_Traslado_TasaOCuota              0.160000
              Concepto_Traslado_Importe   000.00

Procesando concepto 43201830 Disco de Estado Sólido SSD 120GB
              Concepto_ClaveProdServ      43201830
              Concepto_Cantidad           1
              Concepto_ClaveUnidad        H87
              Concepto_Unidad             pieza
              Concepto_Descripcion        Disco de Estado Sólido SSD 120GB
              Concepto_ValorUnitario      650.00
              Concepto_Importe            650.00
              Concepto_Traslado_Base      650
              Concepto_Traslado_Impuesto  002
              Concepto_Traslado_TipoFactor              Tasa
              Concepto_Traslado_TasaOCuota              0.160000
              Concepto_Traslado_Importe   000.00

Procesando concepto 81111809 Instalación de Software a Equipo de Cómputo
              Concepto_ClaveProdServ      81111809
              Concepto_Cantidad           1
              Concepto_ClaveUnidad        E48
              Concepto_Unidad             servicio
              Concepto_Descripcion        Instalación de Software a Equipo de Cómputo
              Concepto_ValorUnitario      100.00
              Concepto_Importe            100.00
              Concepto_Traslado_Base      100.00
              Concepto_Traslado_Impuesto  002
              Concepto_Traslado_TipoFactor              Tasa
              Concepto_Traslado_TasaOCuota              0.160000
              Concepto_Traslado_Importe   00.00



Publicado por: RUGALB
Fecha de publicación: 17/Agosto/2022 a las 23:09
Muchas Gracias por todas tus observaciones Bacterio Smile personalmente me enriquecen mucho, cabe aclarar que la "solucion chapuza" que coloque es con la que habia venido trabajando, (antes de que hicieras tu primer comentario) pero como en post anterior comente que la publicaría lo hice,  para esto; ya cuando habías comentado. (Aún no comenzaba a analizar tu código todavía).

De antemano sé que aunque mi código "funciona" es muy redundante y hasta mal estructurado, desgraciadamente tuve que implementar algo a la brevedad por la premura del tiempo. Es verdad que desconocía todo esto del tratamiento de XML y me toca ir aprendiendo sobre la marcha. 

Te agradezco en verdad la molestia que te tomaste al analizar el problema de fondo y tomarte el tiempo para escribri todo ese código, Me pongo ahora mismo a analizarlo e implementarlo, espero que me des la oportunidad de aclarar alguna duda si la hubiese en el camino.

Mil Gracias, un honor para mi leerte.

Saludos

Edito para hacer un par de notas 

1.- Ya pensaba "migrar" a la brevedad los datos por así decirlo "fijos"(Comprobante, Emisor, Receptor, Impuestos y Complemento) a una tabla aparte para no almacenar tanta información en la tabla principal (Conceptos y Concepos_Impuestos) aunque al final del día la trabaja como una tabla temporal solo para el momento de traer los datos del XML, se hace un procedimiento con esa información y luego vacío la tabla

2.-  No me agrada tanto eso de los namespaces realmente, desafortunadamente el fichero no lo genero yo, así viene de "fábrica" generado por la entidad fiscal :(


-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: emiliove
Fecha de publicación: 17/Agosto/2022 a las 23:28
Yo igual no se para que quieres tener toda la información del cfdi, yo igual lo tuviera en dos tablas la factura y el detalle con solo la información necesaria y la ruta del cfdi por si necesitará más datos siempre puedo extaerlos.

Saludos y gracias Bacterio.


Publicado por: RUGALB
Fecha de publicación: 18/Agosto/2022 a las 02:08
Hola Emilio, en relidad requiero toda la información disponible para almacenarla temporalmente, esto solo es el primer paso de un proceso para despues Generar un PDF  (Factura Fiscal), pero se necesitan tratar esos datos antes de hacer la conversión, una vez generado el PDF ya no es necesaria la información y borro los registros de la tabla para que quede lista para recibir los datos de otro fichero. Por otra parte, tienes razón, voy a crear la otra tabla para almacenar los datos "fijos", para no tener tanto campo amontonado e inecesario en la tabla 1Dead

Gracias por tus comentarios!


-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: emiliove
Fecha de publicación: 24/Agosto/2022 a las 17:49
Una pregunta Bacterio, si por ejemplo tenemos en el nodo cfdi:Concepto valor en los atributos
pero ya no existen los nodos subsiguientes cfdi:Impuestos/cfdi:Traslados/cfdi:Traslado me marca Variable de objeto o bloque with no establecido y no se me ocurre como evitar, que recorra esos nodos. Alguna idea.

Gracias.



Publicado por: emiliove
Fecha de publicación: 24/Agosto/2022 a las 18:35
Me respondo con un Length > 0 reviso primero que existan los nodos y procedo a recorrerlos.

Saludos.


Publicado por: Bacterio
Fecha de publicación: 24/Agosto/2022 a las 21:27
Verificando si SelectSingleNode ha devuelto Nothing

Set ndConceptoTraslado = ndConcepto.selectSingleNode("cfdi:Impuestos/cfdi:Traslados/cfdi:Traslado")
if not ndConceptoTraslado is nothing then
For Each att In ndConceptoTraslado.Attributes
Debug.Print , ndConcepto.baseName & "_" & ndConceptoTraslado.baseName & "_" & AttributeNameToFieldName(att.Name), att.Value
Next
End If


Publicado por: emiliove
Fecha de publicación: 24/Agosto/2022 a las 22:08
Mucho más fácil, si detectaba que ndConceptoTraslado se hacía nothing cuando no tenía nodos y entraba a IsMissing(strMensajeError) pero batelle en entender por qué.

Muchas gracias.


Publicado por: RUGALB
Fecha de publicación: 25/Agosto/2022 a las 00:51
Emilio, ¿haces alusión a cuando por ejemplo son Facturas Ventas al Público en General?

lo pregunto por que No había probado con algun XML de esa naturaleza...Shocked 


Saludos a todos.


-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: emiliove
Fecha de publicación: 25/Agosto/2022 a las 13:49
Me refiero a la forma que crearon el Xml, los nodos hijos que no tienen datos pueden estar o no estar, en ambos casos el xml es válido y si cumple con los requisitos de nodos obligatorios del xsl también es correcto para el proveedor del timbrado.
Es solo la forma que crearon el xml, en factura de comisiones o créditos bancarias, en servicios de agua y en comprobantes de pago tenía el problema claro que en comprobantes de pago me falta agregar algunos nodos hijos extras.

Saludos.


Publicado por: RUGALB
Fecha de publicación: 26/Agosto/2022 a las 04:26
Entiendo, no veía todo el panorama jeje.

Saludos cordiales


-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: RUGALB
Fecha de publicación: 26/Agosto/2022 a las 04:32
Ofrezco una disculpa por dejar el hilo abandonado.

Agradezco todas las respuestas brindadas.
Bacterio, prometo indagar más sobre este tema del Tratamiento de XML desde VBA, me brindaste muy buenas bases.

solo tuve que cambiar lo siguiente en tu código:
Agregar el namespace:
xmlns:tfd='http://www.sat.gob.mx/TimbreFiscalDigital' "
y cambie esto:
 Set ndComplemento_TimbreFiscalDigital = ndComplemento.selectSingleNode("cfdi:TimbreFiscalDigital")
por esto:
 Set ndComplemento_TimbreFiscalDigital = ndComplemento.selectSingleNode("tfd:TimbreFiscalDigital")
  para que no causara conflicto con los XML a importar.

Mi gratidud y mis Saludos para todos.


-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: RUGALB
Fecha de publicación: 26/Agosto/2022 a las 05:56
Dejo un clip de Video de como quedo funcionando la Aplicación.

https://e.top4top.io/m_242941thc1.mp4" rel="nofollow - Clic Aquí

¡Mil Gracias a Todos!

Saludos.Smile


-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: Bacterio
Fecha de publicación: 29/Agosto/2022 a las 10:53
Me ha llamado la atención que al generar el PDF no aparece el mensaje "Extrayendo ... al archivo ..."

¿Cómo evitas que aparezca ese mensaje?


Publicado por: RUGALB
Fecha de publicación: 30/Agosto/2022 a las 04:46
Hola Bacterio!

Quiero Pensar que el programa que utilicé para grabar la pantalla se come algunos Frames, si se muestra pero en el video no se alcanza a apreciar. dejo una captura de pantalla.Smile




-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: RUGALB
Fecha de publicación: 30/Agosto/2022 a las 04:49
Curiosamente al tomar el screenshot solo capturó la ventana de "Extrayendo... pero se hizo invisible el FormularioPinch 

-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)


Publicado por: emiliove
Fecha de publicación: 30/Agosto/2022 a las 16:14
Podemos cerrar ya.


Publicado por: RUGALB
Fecha de publicación: 30/Agosto/2022 a las 20:38
Si me hacen favor, se puede cerrar el hilo. muchas gracias a todos.

-------------
Saludos desde Toluca, Estado de Mexico "El pesimista se queja del viento, el optimista espera que cambie, el realista ajusta las velas"(William Ward)



Imprimir página | Cerrar ventana