0 Seguidores · 21 Publicaciones

Extensible Markup Language (XML) es un lenguaje de etiquetas que define el conjunto de reglas que deben seguirse para codificar documentos en un formato que sea legible tanto para el humano como por la máquin.

Obtener más información.

Artículo Luis Angel Pérez Ramos · oct 30, 2025 3m read

Hola comunidad,

Quería compartir mi experiencia trabajando en proyectos con grandes volúmenes de datos. A lo largo de los años, he tenido la oportunidad de manejar enormes cantidades de datos de pacientes, datos de aseguradoras y registros transaccionales mientras trabajaba en la industria hospitalaria. He tenido la oportunidad de crear informes muy extensos que requerían usar lógicas avanzadas para obtener datos de múltiples tablas, cuyos índices no me ayudaban a escribir un código eficiente.

Esto es lo que he aprendido sobre cómo gestionar grandes volúmenes de datos de manera eficiente.

Elegir el método de acceso a datos adecuado

Como todos sabemos en esta comunidad, IRIS ofrece múltiples formas de acceder a los datos. Elegir el método correcto dependerá de lo que necesitemos.

  • Acceso directo a los Globales: el más rápido para operaciones masivas de lectura/escritura. Por ejemplo, si tengo que recorrer índices y obtener datos de pacientes, puedo iterar sobre los globales para procesar millones de registros. Esto ahorra mucho tiempo.
Set ToDate=+HSet FromDate=+$H-1ForSet FromDate=$O(^PatientD("Date",FromDate)) Quit:FromDate>ToDate  Do
. Set PatId=""ForSet PatId=$Order(^PatientD("Date",FromDate,PatID)) Quit:PatId=""Do
. . Write$Get(^PatientD("Date",FromDate,PatID)),!
  • Uso de SQL: útil para requisitos de generación de informes o análisis, aunque más lento para conjuntos de datos muy grandes.
0
0 21
Artículo Yuri Marx · oct 20, 2025 4m read

El lenguaje ObjectScript cuenta con un soporte increíble para JSON gracias a clases como %DynamicObject y %JSON.Adaptor. Este soporte se debe a la enorme popularidad del formato JSON, que sustituyó el dominio previo de XML. JSON trajo consigo una representación de datos menos verbosa y una mayor legibilidad para las personas que necesitaban interpretar su contenido. Para reducir aún más la verbosidad y aumentar la legibilidad, se creó el formato YAML.

0
0 23
Artículo Ricardo Paiva · jun 4, 2025 3m read

IRIS admite transformaciones CCDA y FHIR de forma nativa, pero acceder y visualizar estas funcionalidades requiere tiempo de configuración y conocimiento del producto. La aplicación IRIS Interop DevTools fue diseñada para cerrar esa brecha, permitiendo a los implementadores comenzar de inmediato y explorar las capacidades de transformación integradas del producto.

Además del entorno de transformación IRIS XML, XPath y CCDA, el paquete Interop DevTools ahora proporciona:

0
0 41
Pregunta José Francisco Jodar · abr 9, 2025

Estoy intentando validar el contenido de un XML contra un esquema XSD, para validar la estructura (nodos obligatorios, etc...)

Estoy intentando utilizar la clase Ens.Util.XML.Validator, como se sugiere en este post:

https://community.intersystems.com/post/validate-xml-message-against-xs…

Pero al invocar al metodo ValidateStream, si en el schemaSpec le dejo el parametro vacio, simplemente valida que sea una estructura XML correcta.

Al pasarle cualquier valor en schemaSpec, da un error de "SAX XML Parser Error: Unable to Resolve SystemId"

5
0 83
Artículo Jose-Tomas Salvador · sep 30, 2024 3m read

Existen muchas aplicaciones para trabajar con mensajes HL7 V2, pero las herramientas para trabajar con XML en el Portal de Gestión o los IDE de IRIS son limitadas. Aunque hay muchas utilidades externas e IDEs que funcionan con mensajes XML e incluso documentos C-CDA, hay una razón convincente para poder hacer pruebas directamente en el marco de trabajo C-CDA de IRIS.

Hacer pruebas dentro del entorno de IRIS os proporciona el contexto necesario:

0
0 78
Artículo Alberto Fuentes · sep 23, 2024 8m read

¡Hola, desarrolladores!

En esta serie de artículos hemos hablado del framework iris-datapipe, de cómo nos ayuda a crear "pipes" de datos para la ingesta y procesamiento, y de cómo instalarlo. Vamos a profundizar en cómo implementar uno de esos "pipes" paso a paso.

Si llegaste directamente a este artículo, te recomiendo revisar los anteriores y recordar que iris-datapipe incluye un QuickStart para que puedas explorar sus funcionalidades rápidamente 👌.

El ejemplo que abordaremos está incluido en el QuickStart, por lo que puedes utilizarlo como referencia.

Definir un nuevo Pipe

Comienza por definir un nuevo Pipe en la interfaz gráfica. Básicamente, solo se trata de asignar un código y una descripción.

Opcionalmente, puedes especificar un recurso de seguridad de IRIS que se requerirá para poder operar con ese Pipe (esto es útil si necesitas crear pipes que solo sean accesibles a determinados usuarios).

En el ejemplo, definimos un pipe llamado REST-API, que se encargará de procesar datos recibidos desde una API REST, donde llegarán datos sobre personas.

image

Procesamiento de los datos

Para procesar los datos utilizando un "pipe", necesitamos seguir los siguientes pasos:

1) Definir un modelo DataPipe

Debemos definir un modelo para los datos que queremos procesar.

Un modelo no es más que una clase que hereda o extiende de DataPipe.Model.cls donde tendrás que implementar algunos métodos.

Tu modelo debe implementar:

  • Cómo serializar/deserializar tus datos (por ejemplo, usando XML o JSON).
  • Cómo normalizar y validar tus datos.
  • Y finalmente, qué operación quieres ejecutar sobre tus datos una vez están normalizados y validados.

En mi ejemplo, el modelo que utilizaré será DataPipe.Test.REST.Models.Person.cls.

Dado que el modelo es una clase convencional en InterSystems IRIS, puedes añadir herencia u otro comportamiento que necesites. En mi caso, heredo de DataPipe.Test.REST.Msg.PersonData, donde tengo definidas las propiedades que me interesa tratar.

El modelo DataPipe.Test.REST.Models.Person.cls tiene diferentes métodos:

  • Serialize, Deserialize: Se utilizan para indicar cómo serializar/deserializar el modelo. En este caso, utilizo JSON.
  • Normalize: Especifica cómo quiero normalizar el modelo. En mi caso, solo quiero llamar a una transformación de datos.
/// Normalize model
Method Normalize(Output obj As DataPipe.Model) As %Status
{
    set ret = $$$OK
    try {
        // call normalizaton data transform
        set sc = $classmethod("DataPipe.Test.REST.DT.PersonNormalize", "Transform", $this, .obj)
        $$$ThrowOnError(sc)

    } catch ex {
        set ret = ex.AsStatus()
    }
    quit ret
}
  • Validate: Indica cómo quiero validar si mi modelo es correcto o no. Puedo añadir "warnings" también. Puedes implementar lo que necesites:
/// Validate model
Method Validate(Output errorList As %List) As %Status
{
    #define AddError(%list, %code, %desc) set error = ##class(DataPipe.Data.ErrorInfo).%New() set error.Code=%code set error.Desc=%desc do %list.Insert(error)
	
    set ret = $$$OK
    try {
        set errorList = ##class(%ListOfObjects).%New()

        // date of birth
        if ..DOB="" { 
            $$$AddError(errorList, "V001", "DOB required")
        } else {
            set yearDOB = $extract($zdate(..DOB,8),1,4)
            if (yearDOB < 1930) $$$AddError(errorList, "V002", "DOB must be greater than 1930")
            if (yearDOB < 1983) $$$AddError(errorList, "W083", "Warning! Older than 1983")
        }

        // model is invalid if errors (not warnings) found
        for i=1:1:errorList.Count() {
            set error = errorList.GetAt(i)
            set errorCode = error.Code

            // in this sample model, all warnings start with "W"
            if errorCode'["W" {
                $$$ThrowStatus($$$ERROR($$$GeneralError, "Invalid"))
            }
		}
       
    } catch ex {
        set ret = ex.AsStatus()
    }

    quit ret
}
  • GetOperation: Indica qué Business Operation quiero que ejecute la operación sobre los datos (lo entenderás mejor cuando comentemos los componentes de interoperabilidad).
/// Return the Business Operation name that will run the operation with the model
/// Each Business Operation can be used to hold different queues
Method GetOperation() As %Status
{
    quit "Person Operation"
}
  • RunOperation: Esta es la operación que quiero ejecutar sobre mis datos. Puedo guardarlos en la base de datos o llamar a otro componente de interoperabilidad para continuar el procesamiento más adelante. En mi ejemplo, los guardo en una global y también los envío a otro Business Process.
/// Run final operation with the model
/// This method can be used to persit data from the model to an operational data store
Method RunOperation(Output errorList As %List, Output log As %Stream.Object, bOperation As Ens.BusinessOperation = "", Output delayedProcessing As %Boolean = 0) As %Status
{
    #define AddError(%list, %code, %desc) set error = ##class(DataPipe.Data.ErrorInfo).%New() set error.Code=%code set error.Desc=%desc do %list.Insert(error)
    #define AddLog(%log, %msg) do %log.WriteLine("["_$zdt($h,3)_"] "_%msg)
	
    set errorList = ##class(%ListOfObjects).%New()
    set log = ##class(%Stream.GlobalCharacter).%New()

    set ret = $$$OK
    try {
        TSTART
        $$$AddLog(log, "Transaction Started")

        // simulate an operation error
        if ##class(Ens.Util.FunctionSet).In(..Name, ##class(DataPipe.Test.HL7.Helper).OperationErrorNames()) {
            $$$ThrowStatus($$$ERROR($$$GeneralError, "Simulated Operation Error"))
        }

        // store serialized model
        $$$ThrowOnError(..Serialize(.stream))
        set ^zDataPipe($i(^zDataPipe)) = stream.Read()
        $$$AddLog(log, "Model Stored in ^zDataPipe("_$get(^zDataPipe)_")")

        TCOMMIT
        $$$AddLog(log, "Transaction Commited")

        // you can send messages to other production components (while you are not on an open transaction)
        // you can use this feature to continue processing the record in other component (delayed processing)
        set delayedProcessing = 1
        if $isobject(bOperation) {
            set req = bOperation.OperRequest
            $$$ThrowOnError(bOperation.SendRequestAsync("REST Delayed Oper Update", req))
        }

    } catch ex {
        TROLLBACK 
        $$$AddLog(log, "Rollback!")

        set ret = ex.AsStatus()
        $$$AddLog(log, "Error catched: "_$system.Status.GetOneStatusText(ret))

        // include exception errors into errorList
        do $system.Status.DecomposeStatus(ret, .errors)
		for i=1:1:errors {
			$$$AddError(errorList, "Exception", errors(i))
		}
    }
    quit ret
}

2) Añadir componentes de interoperabilidad

Después de definir tu modelo, necesitas configurar una producción de interoperabilidad usando componentes de DataPipe. iris-datapipe incluye componentes preconstruidos que pueden operar con un modelo DataPipe como el que hemos definido anteriormente.

El único proceso que debes implementar es el proceso de ingestión.

2.1) Crear un Proceso de Ingestión

Necesitas crear un nuevo Business Process que utilice como contexto DataPipe.Ingestion.BP.IngestionManagerContext.

Este proceso recibirá la entrada de datos que decidas (en este ejemplo, el mensaje que envía una API REST que actúa como Business Service) y debe implementar:

Identificación de los datos que procesas (InboxAttributes):

  • Debes identificar el registro que estás procesando y proporcionar los InboxAttributes.
  • El "pipe" al que pertenece este registro debe indicarse en este momento.
  • Estos atributos describirán el registro que estás tratando y luego se utilizarán para la búsqueda desde la interfaz gráfica.
  • Para realizar lo anterior, puedes utilizar transformaciones de datos, código, ¡lo que necesites!

Convertir los datos de entrada en el modelo de datos que definiste previamente:

  • Debes utilizar transformaciones, código o lo que prefieras para transformar la información de entrada al modelo de datos que has desarrollado previamente.

image

2.2) Añadir el resto de componentes

El resto de los componentes de interoperabilidad son proporcionados por iris-datapipe y ya están preconstruidos. Estos componentes llamarán a los distintos métodos implementados en tu modelo.

En general, necesitarás añadir a la producción, por cada "pipe" diferente que quieras implementar:

Aquí tienes la producción de ejemplo que se utiliza en el QuickStart.

Con todo esto, cuando comiencen a llegar datos y se procesen, podrás verlos directamente desde la interfaz gráfica.

image

¡Espero que os sea útil!

0
0 74
Artículo Jose-Tomas Salvador · jul 8, 2024 1m read

¿Alguna vez habéis importado un esquema XML desde un archivo XSD? Es posible que queráis volver a consultar el archivo original algún tiempo después, pero habéis olvidado dónde lo pusisteis.

No os preocupéis, esa información se guarda como parte del proceso de importación.

Todo el esquema XSD importado se guarda en el global ^EnsEDI.XML.Schema. Ese global contiene todos los XSDs importados en vuestro espacio de nombres. El primer subíndice es el nombre del esquema que se ve en el portal.

Para buscar la ubicación del archivo XSD de origen, basta con mirar en el siguiente lugar:

0
0 199
Artículo Hansel Rudy Stange Gaete · mar 20, 2024 2m read

En el proceso de consumir los servicios del SII me encontré con algunas dificultades y quiero compartirlas para facilitar el trabajo posterior.

El contexto es bastante local, por eso no expondré documentación comprometedora, solo los detalles de cómo adaptarse a los requerimientos del Servicio.

Hay un proceso de autenticación que demanda adjuntar la firma en el body de un documento, que a su vez va en un campo string en el body del mensaje. Para ello había que mezclar la documentación con la obtención del string, usando oportunamente estas líneas :

0
0 188
Artículo Alberto Fuentes · feb 21, 2024 2m read

Quería compartiros hoy un pequeño truco para personalizar cómo se muestran los mensajes en el Visor de Mensajes. En concreto, cómo mostrar mensajes JSON directamente en el Visor de Mensajes en lugar de serializados como XML.

image

Los mensajes son los objetos que utilizamos para comunicar componentes de una producción de interoperabilidad. En mi caso me había definido un mensaje que utilizaba después para pasar a JSON y enviar a una API. Este mensaje está definido como un mensaje convencional y también como %JSON.Adaptor para poder exportar / importar directamente a JSON.

Class interop.msg.DeviceOrderReq Extends (Ens.Request, %JSON.Adaptor)
{

Parameter %JSONNULL As BOOLEAN = 1;

Property externalOrderId As %String(MAXLEN = "");

Property orderStatus As %String;

Property requestedServiceId As %String(MAXLEN = "");

Property patientData As interop.msg.elements.PatientData;

}

El mensaje funciona correctamente cuando hago diferentes pruebas en mi producción, sin embargo en el Visor de Mensajes aparece con la representación por defecto XML:

image

La representación es correcta, pero me sería mucho más intuitivo ver el mensaje representado directamente en JSON. Para ello, podemos sobreescribir el método %ShowContents en nuestro mensaje.

En mi caso, para poder reutilizar código me he creado una clase llamada JSONMessage. Esta clase sobreescribe el %ShowContents para mostrar la representación en JSON formateada del objeto.

Class interop.msg.JSONMessage Extends (Ens.Request, %JSON.Adaptor)
{

/// This method is called by the Management Portal to determine the content type that will be returned by the <method>%ShowContents</method> method.
/// The return value is a string containing an HTTP content type.
Method %GetContentType() As %String
{
	Quit "text/html"
}

/// This method is called by the Management Portal to display a message-specific content viewer.<br>
/// This method displays its content by writing out to the current device.
/// The content should match the type returned by the <method>%GetContentType</method> method.<br>
Method %ShowContents(pZenOutput As %Boolean = 0)
{
   do ..%JSONExportToString(.jsonExport)
    set formatter = ##class(%JSON.Formatter).%New()
    do formatter.FormatToString(jsonExport, .json)
    &html<<pre>#(json)#</pre>>
}

}

Por último, sólo queda cambiar la definición del mensaje original para que herede de JSONMessage:

Class interop.msg.DeviceOrderReq Extends (JSONMessage, Ens.Request)
{

Parameter %JSONNULL As BOOLEAN = 1;

Property externalOrderId As %String(MAXLEN = "");

Property orderStatus As %String;

Property requestedServiceId As %String(MAXLEN = "");

Property patientData As interop.msg.elements.PatientData;

}
2
0 295
Pregunta Oscar Matute · feb 2, 2023

Hola !
Estamos investigando el tema de la generación de la factura electrónica.

Vemos que desde la página la factura e (Factura Electrónica - Últimas versiones Facturae), hay un enlace para descargar el fichero (Esquema XSD formato Facturae 3.2.2 [XML] [185,61 KB]).

 

Una vez descargado el fichero lo intentamos incorporar al studio de cache y nos da un error al importarlo.

Lo importamos desde: Herramientas -> Complementos -> Asistente de Esquemas XML. Seleccionamos el fichero que nos hemos descargado y aparentemente la estructura la pone bien pero luego da error importarlo (“DTD's explicity prohibited)” 

1
0 327
Artículo Ricardo Paiva · ene 18, 2023 4m read

Schematron es un lenguaje de validación basado en reglas para hacer aserciones/afirmaciones sobre la presencia o ausencia de ciertos patrones en documentos XML. Un Schematron se refiere a una colección de una o más reglas que contienen pruebas. Los Schematron están escritos en una forma de XML, lo que los hace relativamente fáciles de inspeccionar, comprender y escribir para todos, incluso los que no son programadores.

0
0 401
Pregunta Kurro Lopez · feb 7, 2022

Buenas a todos.

Tengo un servicio creado por el wizard de WebServices para la invocación a un proveedor de citas.

Para valicar la petición, tengo que revisar si en el nodo de mensajes viene algún texto porque eso significa que ha habido algún problema con las citas solicitadas.

Mi clase de respuesta del proveedor es algo como esta (por motivos de confidencialidad, no puedo mostrar la clase real, pero si el valor exacto del nodo que estoy preguntando):

1
0 271
Pregunta Flávio Lúcio Naves Júnior · nov 30, 2021

¡Hola a todos!

Estoy intentando exportar una clase a xml y quitar la etiqueta "xmlns" de la clase madre. Estas son mis clases para crear el XML.

Class Class.Test Extends (%RegisteredObject, %XML.Adaptor)
{ 

Parameter NAMESPACE = "http://mynamespace.com/test"; 
Property Person As Class.Person; Property Address As Class.Address; 

}
Class Class.Person Extends (%RegisteredObject, %XML.Adaptor)
{ 

Parameter NAMESPACE = "http://mynamespace.com/test"; 
Property name As %String; Property age As %String; 

}
Class Class.Address Extends (%RegisteredObject, %XML.Adaptor)
{ 

Parameter NAMESPACE = "http://mynamespace.com/test"; 
Property location As %String; 

}
2
0 263
Pregunta Laura Blázquez García · ago 20, 2021

Tengo una clase que parsea un XML para extraer toda la información. En este XML hay un atributo que tiene saltos de línea:

<record date="2021-08-11T14:25:21" entity="TEXTO"><OBSERVACIONES o="" n="Esto es un texto de prueba:
1. Contiene información importante
2. Es útil para el usuario
3. Sigue siendo una prueba"/></record>

Usando %XML.TextReader puedo acceder a toda la información del XML, pero esos saltos de línea se pierden. Esto es lo que obtengo:

2
0 878
Artículo Joel Espinoza · oct 28, 2019 4m read

¡Hola Comunidad!

Una función útil de nuestra estructura REST es la capacidad que tienen las clases de Dispatch para identificar los prefijos de una solicitud y redireccionarlos a otra clase de Dispatch. Este enfoque permite mejorar el orden y la lectura del código, permite mantener separadas las versiones de una interfaz fácilmente y ofrece una forma de proteger llamadas a APIs a las que solo ciertos usuarios podrán acceder.

1
0 300
Artículo Alberto Fuentes · ene 28, 2021 2m read

Hola a todos! 

Comparto una pequeña utilidad (servicio REST) para descargar mensajes de una producción de interoperabilidad como ficheros.

Sólo necesitas:

  1. Crear una aplicación web en el Portal de Gestión (e.g. /downloadmsg) que tenga configurado DispatchClass=Util.DownloadMsg.
  2. Llamar a la utilidad pasándole el namespace y el identificador de cabecera del mensaje a descargar. http://localhost:52773/downloadmsg/ns/dev/msgid/17441
/// 
/// Util to download messages given a message header id
/// 
/// Setup:
/// 1. Create a webapplication (e.g. /downloadmsg) and set DispatchClass=Util.DownloadMsg
/// 2. Go to http://localhost:52773/downloadmsg/ns/user/msgid/19 to download the message reference by header 19
Class Util.DownloadMsg Extends %CSP.REST
{

XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
    <Route Url="/ns/:ns/msgid/:msgId" Method="GET" Call="DownloadMessage"/>
</Routes>
}

/// Download a message given a Ens.MessageHeader id
ClassMethod DownloadMessage(ns As %String, msgId As %String = "") As %Status
{
    set ret = $$$OK
    set currentNs = $namespace

    try {
        set $namespace = ns
        $$$ThrowOnError(..ExportMsgToStream(msgId, .stream, .filename))

        // set headers to download stream as filename
        do %response.SetHeader("Content-Type", "application/octet-stream")
        do %response.SetHeader("Content-Disposition", "attachment; filename="""_filename_"""")
        do %response.SetHeader("Content-Length", stream.Size)
        do stream.Rewind()
        do stream.OutputToDevice()

    } catch ex {
        set ret = ex.AsStatus()
    }

    // restore namespace
    set $namespace = currentNs

    quit ret
}

/// Export a message to stream given a Ens.MessageHeader id
ClassMethod ExportMsgToStream(msgId As %String, Output stream As %Stream.Object, Output filename As %String) As %Status
{
    set ret = $$$OK
    try {
        // stream
        set stream = ##class(%Stream.GlobalCharacter).%New()
        set filename = ""

        // message header
        set headerObj = ##class(Ens.MessageHeader).%OpenId(msgId,,.sc)
        $$$ThrowOnError(sc)

        // message body
        set obj = $classmethod(headerObj.MessageBodyClassName, "%OpenId", headerObj.MessageBodyId)
        set classname = $classname(obj) 

        // output to stream
		if classname="EnsLib.HL7.Message" {
			set sc = $method(obj, "OutputToLibraryStream", .stream)
			$$$ThrowOnError(sc)
			set filename = msgId_".hl7"
		}
		else {
			set writer = ##class(%XML.Writer).%New()
			set writer.Indent=1
			set writer.NoXMLDeclaration=1
			$$$ThrowOnError(writer.OutputToStream(.stream))
			$$$ThrowOnError(writer.RootObject(obj))
			set filename = msgId_".xml"
		}
    } catch ex {
        set ret = ex.AsStatus()
    }
    quit ret
}

}
2
0 196
Artículo Alberto Fuentes · oct 14, 2019 6m read

¡Hola a tod@s!

Me gustaría comentar con vosotros algunas de las mejoras en procesamiento JSON que incorpora IRIS desde la versión 2019.1. Utilizar JSON como formato de serialización es muy común a la hora de construir aplicaciones hoy en día, especialmente si desarrollamos o interactuamos con servicios REST.

Dar formato a cadenas a JSON

0
0 337