Ir al contenido

XML Activities

Versión: 1.1.0  ·  ID: zoan-packages-xml  ·  Autor: Zoan Software

El paquete XML lee, consulta y modifica documentos XML, un formato de intercambio de datos muy usado en integraciones (facturación electrónica, servicios web SOAP, configuraciones, reportes que exportan otros sistemas). Te permite extraer datos de un XML y modificarlo sin tener que parsear el texto a mano.

Las actividades trabajan sobre el texto XML (una cadena), no sobre un archivo abierto. El patrón típico es:

  1. Read XML lee el archivo y devuelve su contenido como string.
  2. Consultas valores con XML Query Text / XML Query All, conviertes todo a un diccionario con XML to Dictionary, o lo modificas con XML Set Value / XML Add Element (que devuelven el XML modificado).
  3. Write XML guarda el resultado a un archivo.
Read XML path = ruta → output: xml
XML Query Text xml = xml xpath = "//Factura/Total" → output: total

Las actividades que modifican (XML Set Value, XML Add Element) devuelven el documento con sangría y sin la declaración <?xml …?>. Write XML vuelve a escribir la declaración, con la codificación del archivo, al guardarlo.

XPathSelecciona
//NumeroTodos los elementos <Numero> en cualquier nivel
//Factura/NumeroLos <Numero> hijos directos de <Factura>
//Factura[@id='5']La <Factura> con atributo id="5"
//Item[2]El segundo <Item>
//Total/@monedaEl valor del atributo moneda de <Total>
count(//Item)Cuántos <Item> hay

El XPath (1.0) se evalúa sobre el documento completo. Puede seleccionar elementos, atributos, nodos de texto o calcular un valor (count(…), sum(…), una comparación); las actividades de consulta devuelven siempre su texto:

  • Un elemento con hijos devuelve el texto de todos sus descendientes, unido.
  • Un número entero sale sin decimales (3, no 3.0); un booleano, como true o false.

Muchos XML de integraciones (facturas UBL, SOAP) usan namespaces: los nombres llevan un prefijo, como cbc:ID o cac:InvoiceLine, declarado con xmlns:cbc="…". Desde la versión 1.1.0, las actividades con xpath los registran solas:

  • Los prefijos declarados en el documento (en la raíz o más adentro) se usan tal cual en el XPath: //cbc:ID, //cac:LegalMonetaryTotal/cbc:PayableAmount. No hace falta local-name().
  • El namespace por defecto (xmlns="…", sin prefijo) se escribe en el XPath con el prefijo d: — /d:Invoice/cbc:ID. Puedes cambiarlo con defaultPrefix (vacío equivale a d). Si el documento ya declara un prefijo d para otro namespace, ese manda: elige otro defaultPrefix (p. ej. n) para llegar al namespace por defecto.
  • Si el XPath no encuentra nada, el documento tiene namespace por defecto y el XPath no usa su prefijo, el log te lo recuerda con un aviso: El XML tiene un namespace por defecto: en el XPath escribe sus elementos con el prefijo ‘d:’ (p. ej. //d:Invoice).
  • Con el parámetro opcional namespaces (diccionario prefijo → URI) agregas prefijos propios o reemplazas los del documento; los tuyos mandan.
  • Si el XPath usa un prefijo que no existe, el error dice cuál y qué prefijos hay.
  • Si un mismo prefijo se declara con dos URI distintas, vale la primera en el orden del documento; para usar la otra, defínela en namespaces.
XML Query Text
xml = documento
xpath = "//inv:Total"
namespaces = dictSet(newDict(), "inv", "urn:ejemplo:facturas")
→ output: total

A veces un XML trae otro XML como texto, dentro de un CDATA. Es el caso de la factura electrónica de Colombia (DIAN, UBL 2.1): llega como un AttachedDocument y la factura real va en cac:Attachment/cac:ExternalReference/cbc:Description. Se resuelve en dos pasos: extraer ese texto y consultarlo directamente (no hace falta guardarlo en un archivo).

Read XML path = ruta → output: contenedor
XML Query Text xml = contenedor
xpath = "//cac:Attachment/cac:ExternalReference/cbc:Description" → output: factura
XML Query Text xml = factura xpath = "/d:Invoice/cbc:ID" → output: numero
XML Query Text xml = factura
xpath = "//cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID" → output: nitEmisor
XML Query Text xml = factura xpath = "/d:Invoice/cbc:IssueDate" → output: fecha
XML Query Text xml = factura xpath = "/d:Invoice/cbc:DueDate" → output: vencimiento
XML Query Text xml = factura xpath = "//cac:LegalMonetaryTotal/cbc:PayableAmount" → output: total
XML Query Text xml = factura xpath = "/d:Invoice/cbc:UUID" → output: cufe
XML Query All xml = factura xpath = "//cac:InvoiceLine/cbc:LineExtensionAmount" → output: valoresLineas

Si necesitas muchos datos, convierte la factura una sola vez con XML to Dictionary. El texto que sale del CDATA puede empezar con saltos de línea, espacios o un BOM antes de <?xml …?>: las actividades los ignoran. También sirve si el XML embebido viene escapado (&lt;Invoice…) en lugar de en un CDATA.

Desde la versión 1.1.0, todas las actividades leen el XML de forma segura (también Read XML y Write XML, que validan el documento):

  • No se admiten DTD (<!DOCTYPE …>) ni entidades: un XML con DTD se rechaza con un error claro. Así se evitan los ataques de entidades externas (XXE), que intentan leer archivos del equipo, y las «bombas» de expansión de entidades.
  • Tamaño máximo: 50 MB por documento (50 M caracteres si llega como texto).
  • Anidamiento máximo: 256 niveles.

Estos mensajes pueden salir de cualquier actividad que lea XML y detienen la actividad. Si el XML viene de fuera y puede llegar dañado, envuelve la lectura en Try Catch.

MensajeCausa y solución
Mensaje del analizador XML con línea y posición (p. ej. Data at the root level is invalid. Line 1, position 1.)El texto no es un XML bien formado: etiquetas sin cerrar, texto antes de la raíz, & sin escapar. Revisa el origen del texto.
El XML declara una DTD (<!DOCTYPE>). Por seguridad (XXE) no se admiten DTD ni entidades; quita la declaración <!DOCTYPE>.El documento trae <!DOCTYPE …>. Pide el archivo sin DTD o quítala del texto antes de procesarlo.
El XML es demasiado grande (… MB); el límite es 50 MB.El documento supera el tope. Pide archivos más pequeños o divídelos en origen.
El XML tiene más de 256 niveles de anidamiento.Estructura anormalmente profunda.
La expresión XPath «…» no es válida: …Error de sintaxis en el XPath (corchetes o comillas sin cerrar, función inexistente).
El prefijo 'x:' no está declarado en el XML ni en 'namespaces'. Prefijos disponibles: ….El XPath usa un prefijo que el documento no declara. Usa uno de la lista o defínelo en namespaces.
'namespaces' debe ser un diccionario de prefijo → URI del namespace.Pasaste un texto o una lista en namespaces. Usa un diccionario.
'…' no es un prefijo de namespace válido en 'namespaces'. / El prefijo '…' de 'namespaces' no tiene URI de namespace.Una clave de namespaces no es un nombre XML válido (o es xml/xmlns), o su URI está vacía.
'…' no es un prefijo válido para el namespace por defecto.defaultPrefix no es un nombre XML válido (sin espacios ni :), o es xml/xmlns.
ActividadTipoSalidaQué hace
Read XMLxml-readstringLee un archivo XML como texto
Write XMLxml-writestringGuarda un texto XML a un archivo
XML Query Textxml-query-textstringPrimer valor que coincide con un XPath
XML Query Allxml-query-allListTodos los valores que coinciden
XML to Dictionaryxml-to-dictionaryDictionaryConvierte el XML (o un fragmento) en un diccionario navegable
XML Set Valuexml-set-valuestringCambia el valor de un elemento o atributo
XML Add Elementxml-add-elementstringAgrega un elemento hijo