Pregunta
· 17 sep, 2024

Error al transformar de XML a ER7 con esquema HL7 personalizado que hemos creado

Buenos días,

En una integración que estamos haciendo entre dos sistemas recibos un ORU_R01 con los datos de un monitor.  Lo recibimos mediante una llamada a nuestro webservice en formato XML.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:com="http://com.gacelacare.ws">
   <soapenv:Header/>
   <soapenv:Body>
      <com:processMessage>
         <String_1><![CDATA[<ORU_R01 xmlns="urn:hl7-org:v2xml">
    <MSH>
        <MSH.1>|</MSH.1>
        <MSH.2> ^~\&amp;amp;</MSH.2>
        <MSH.3>
            <HD.1>AA</HD.1>
        </MSH.3>
        <MSH.4>
            <HD.1>07</HD.1>
        </MSH.4>
        <MSH.5>
            <HD.1>GCXXX</HD.1>
        </MSH.5>
        <MSH.6>
            <HD.1>07</HD.1>
        </MSH.6>
        <MSH.7>
            <TS.1>20240718110036</TS.1>
        </MSH.7>
        <MSH.9>
            <MSG.1>ORU</MSG.1>
            <MSG.2>R01</MSG.2>
            <MSG.3>ORU_R01</MSG.3>
        </MSH.9>
        <MSH.10>2024071811003601100091714521-C67ADEEC5009447AB2A822A92E7061E9</MSH.10>
        <MSH.11>
            <PT.1>P</PT.1>
        </MSH.11>
        <MSH.12>
            <VID.1>2.5</VID.1>
        </MSH.12>
        <MSH.15>AL</MSH.15>
        <MSH.16>ER</MSH.16>
    </MSH>
    <ORU_R01.PATIENT_RESULT>
        <ORU_R01.PATIENT>
            <PID>
                <PID.1>1</PID.1>
                <PID.3>
                    <CX.1>2222222222</CX.1>
                </PID.3>
            </PID>
            <ORU_R01.VISIT>
                <PV1>
                    <PV1.1>2222222222</PV1.1>
                </PV1>
            </ORU_R01.VISIT>
        </ORU_R01.PATIENT>
        <ORU_R01.ORDER_OBSERVATION>
            <OBR>
                <OBR.1>1</OBR.1>
                <OBR.4>
                    <CE.1>AA</CE.1>
                    <CE.2>AA</CE.2>
                </OBR.4>
                <OBR.10>
                    <XCN.1>11111111V</XCN.1>
                </OBR.10>
                <OBR.25>F</OBR.25>
                <OBR.34>
                    <NDL.1>
                        <CNN.1>11111111V</CNN.1>
                    </NDL.1>
                </OBR.34>
            </OBR>
            <ORU_R01.OBSERVATION>
                <OBX>
                    <OBX.2>CE</OBX.2>
                    <OBX.3>
                        <CE.1>35570</CE.1>
                        <CE.2>Font O2</CE.2>
                        <CE.3>DIC</CE.3>
                    </OBX.3>
                     <OBX.5 LongName="Observation Value">
                        <CE.1>UNAF</CE.1>
                        <CE.2>Ulleres nasals d alt flux</CE.2>
                    </OBX.5>
                    <OBX.3>
                        <CE.1>35570</CE.1>
                        <CE.2>Font O2</CE.2>
                        <CE.3>DIC</CE.3>
                    </OBX.3>
                    <OBX.6>
                        <CE.1>.</CE.1>
                        <CE.2>.</CE.2>
                        <CE.3>DIC</CE.3>
                    </OBX.6>
                    <OBX.11>F</OBX.11>
                    <OBX.14>
                        <TS.1>20240718110036</TS.1>
                    </OBX.14>
                    <OBX.16>
                        <XCN.1>11111111V</XCN.1>
                    </OBX.16>
                    <OBX.18>
                        <EI.1>100091714521</EI.1>
                    </OBX.18>
                </OBX>
            </ORU_R01.OBSERVATION>
            <ORU_R01.OBSERVATION>
				<OBX>
					<OBX.1>5</OBX.1>
					<OBX.2>NM</OBX.2>
					<OBX.3>
						<CE.1>TEMP</CE.1>
						<CE.2>Temperatura C</CE.2>
						<CE.3>ICS</CE.3>
					</OBX.3>
					<OBX.5>36.3599</OBX.5>
					<OBX.6>
						<CE.1>C</CE.1>
						<CE.2>Grados centigrados</CE.2>
						<CE.3>ICS</CE.3>
					</OBX.6>
					<OBX.11>F</OBX.11>
					<OBX.14>
						<TS.1>20230213102353</TS.1>
					</OBX.14>
					<OBX.16>
						<XCN.1>43526320Q</XCN.1>
					</OBX.16>
					<OBX.18>
						<EI.1>100033553418</EI.1>
					</OBX.18>
				</OBX>
			</ORU_R01.OBSERVATION>
        </ORU_R01.ORDER_OBSERVATION>
    </ORU_R01.PATIENT_RESULT>
</ORU_R01>]]></String_1>
      </com:processMessage>
   </soapenv:Body>
</soapenv:Envelope>

 

Luego en nuestra clase lo convertimos a HL7 en formato pipe, tanto para realizar cambios en determinados campos cómo para enviar a otros sistemas. Uno de los sitemas a los que tenemos que enviar el ORU también lo espera en formato XML, por lo que después de hacer los cambios volvemos a transformarlo a XML.

El problema que se nos ha presentado ahora es que nos han empezado a enviar variables en el que el segmento OBX.5 estamos recibiendo una estructura de datos, en lugar de sólo el valor cómo hasta ahora:

Antes:

<OBX.5>115</OBX.5>

Ahora algunas variables, No todas, contienen una estructura de datos:

<OBX.5 LongName="Observation Value">
    <CE.1>UNAF</CE.1>
    <CE.2>Ulleres nasals d alt flux</CE.2>
</OBX.5>

Y cómo en el esquema de HL7 2.5 que viene definido con IRIS espera que el OBX.5 sea un valor:

al transformarlo de fortamo pipe a formato XML lo hace cómo un valor y no estructura: <OBX.5>TUBT^Tub en T</OBX.5> lo que provocar un error en el sistema que recibe el ORU en formato XML .

<ORU_R01.OBSERVATION><OBX><OBX.1 LongName="Set ID - OBX"></OBX.1><OBX.2 LongName="Value Type">CE</OBX.2><OBX.3 LongName="Observation Identifier"><CE.1 LongName="identifier">35570</CE.1><CE.2 LongName="text">Font O2</CE.2><CE.3 LongName="name of coding system">ICS</CE.3></OBX.3><OBX.5>TUBT^Tub en T</OBX.5><OBX.6 LongName="Units"><CE.1 LongName="identifier">.</CE.1><CE.2 LongName="text">.</CE.2><CE.3 LongName="">ICS</CE.3></OBX.6><OBX.11 LongName="Observation Result Status">F</OBX.11><OBX.14><TS.1>20240610125836</TS.1></OBX.14><OBX.16>2563464401</OBX.16><OBX.18><EI.1>100091714521</EI.1></OBX.18></OBX></ORU_R01.OBSERVATION>

No sabemos si es la mejor opción, pero para intentar solucionar este problema, hemos creado un esquema HL7 personalizada para que  el OBX.5 sea estructurado:

<Category name="GACELA.2.5.v2" base="2.5">

<MessageType name='ORU_R01' structure='ORU_R01' returntype='base:ACK_R01' description='Unsolicited transmission of an observation message - ORU Subscription (Response)'/>

<MessageStructure name='ORU_R01' definition='base:MSH~[~{~base:SFT~}~]~{~[~base:PID~[~base:PD1~]~[~{~base:NTE~}~]~[~{~base:NK1~}~]~[~base:PV1~[~base:PV2~]~]~]~{~[~base:ORC~]~base:OBR~[~{~base:NTE~}~]~[~{~base:TQ1~[~{~base:TQ2~}~]~}~]~[~base:CTD~]~[~{~OBX~[~{~base:NTE~}~]~}~]~[~{~base:FT1~}~]~[~{~base:CTI~}~]~[~{~base:SPM~[~{~OBX~}~]~}~]~}~}~[~base:DSC~]'/>

<SegmentStructure name='OBX' description='Observation/Result'>
    <SegmentSubStructure piece='1' description='Set ID - OBX' datatype='base:SI' max_length='4' required='O' ifrepeating='0'/>
    <SegmentSubStructure piece='2' description='Value Type' datatype='base:ID' symbol='?' max_length='2' required='C' ifrepeating='0' codetable='base:125'/>
    <SegmentSubStructure piece='3' description='Observation Identifier' datatype='base:CE' symbol='!' max_length='250' required='R' ifrepeating='0'/>
    <SegmentSubStructure piece='4' description='Observation Sub-ID' datatype='base:ST' symbol='?' max_length='20' required='C' ifrepeating='0'/>
    <SegmentSubStructure piece='5' description='Observation Value' datatype='base:CE' max_length='250' required='O' ifrepeating='0'/>
    <SegmentSubStructure piece='6' description='Units' datatype='base:CE' max_length='250' required='O' ifrepeating='0'/>
    <SegmentSubStructure piece='7' description='References Range' datatype='base:ST' max_length='60' required='O' ifrepeating='0'/>
    <SegmentSubStructure piece='8' description='Abnormal Flags' datatype='base:IS' symbol='*' max_length='5' required='O' ifrepeating='1' codetable='base:78'/>
    <SegmentSubStructure piece='9' description='Probability' datatype='base:NM' max_length='5' required='O' ifrepeating='0'/>
    <SegmentSubStructure piece='10' description='Nature of Abnormal Test' datatype='base:ID' symbol='*' max_length='2' required='O' ifrepeating='1' codetable='base:80'/>
    <SegmentSubStructure piece='11' description='Observation Result Status' datatype='base:ID' symbol='!' max_length='1' required='R' ifrepeating='0' codetable='base:85'/>
    <SegmentSubStructure piece='12' description='Effective Date of Reference Range' datatype='base:TS' max_length='26' required='O' ifrepeating='0'/>
    <SegmentSubStructure piece='13' description='User Defined Access Checks' datatype='base:ST' max_length='20' required='O' ifrepeating='0'/>
    <SegmentSubStructure piece='14' description='Date/Time of the Observation' datatype='base:TS' max_length='26' required='O' ifrepeating='0'/>
    <SegmentSubStructure piece='15' description='Producer&apos;s ID' datatype='base:CE' max_length='250' required='O' ifrepeating='0'/>
    <SegmentSubStructure piece='16' description='Responsible Observer' datatype='base:XCN' symbol='*' max_length='250' required='O' ifrepeating='1'/>
    <SegmentSubStructure piece='17' description='Observation Method' datatype='base:CE' symbol='*' max_length='250' required='O' ifrepeating='1'/>
    <SegmentSubStructure piece='18' description='Equipment Instance Identifier' datatype='base:EI' symbol='*' max_length='22' required='O' ifrepeating='1'/>
    <SegmentSubStructure piece='19' description='Date/Time of the Analysis' datatype='base:TS' max_length='26' required='O' ifrepeating='0'/>
</SegmentStructure>

</Category>

En nuestro Webservice intentamos hacer la transformación de XML a HL7 utilizando este nuevo esquema:

Class WEBSERVICES.IWSReceiverEJBEndPointPort Extends (EnsLib.SOAP.Service) [ ProcedureBlock ]
{

/// This is the namespace used by the Service
Parameter NAMESPACE = "http://xxxxx.ws"; /// Use xsi:type attribute for literal types.
Parameter OUTPUTTYPEATTRIBUTE = 0; /// Determines handling of Security header.
Parameter SECURITYIN = "ALLOW"; /// This is the name of the Service
Parameter SERVICENAME = "ws_receiver"; /// This is the SOAP version supported by the service.
Parameter SOAPVERSION = 1.1; /// Namespaces of referenced classes will be used in the WSDL.
Parameter USECLASSNAMESPACES = 0; /// Non-default use of element/type attribute in parts.
Parameter XMLELEMENT = 0;

...

Method processMessage(String1 As %CacheString(XMLNAME="String_1")) As %CacheString(XMLNAME="result") [ Final, ProcedureBlock = 1, SoapAction = "", SoapBindingStyle = rpc, SoapBodyUse = literal, WebMethod ]
{
   ....

  s hl7 = ##class(EnsLib.HL7.Message).%New()
  s hl7 = ##class(ITB.HL7.Util.Convert).XMLToER7(xml,.sc,"GACELA.2.5.v2") $$$ThrowOnError(sc)

...

}

Al intentar utilizar nuestra esquema customizado nos da el siguiente error:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:s="http://www.w3.org/2001/XMLSchema">
   <SOAP-ENV:Body>
      <SOAP-ENV:Fault>
         <faultcode>SOAP-ENV:Server</faultcode>
         <faultstring>Server Application Error</faultstring>
         <detail>
            <Exception>ERROR #6301: SAX XML Parser Error: &lt;INVALID OREF>zendElement+21^ITB.HL7.Format.HL7XMLv2.1             if (..%SegObj.GetValueAt(..%FieldPath)="") { while processing Anonymous Stream at line 3 offset 25</Exception>
         </detail>
      </SOAP-ENV:Fault>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Utilizando el esquema HL7 2.5 si que hace correctamente la transformación:

s hl7 = ##class(ITB.HL7.Util.Convert).XMLToER7(xml,.sc,"2.5") $$$ThrowOnError(sc)

¿Sabéis porque nos da este error al intentar utilizar nuestra esquema? ¿Hay algún error en la definición o en el uso del esquema para la transformación?

Tampoco sabemos si la aproximación que hemos hecho es la correcta para resolver el problema que tenemos con el OBX.5 , porque recibimos variables con los dos formatos: sólo valor o estructurado. ¿Hay una mejor manera de afrontar este problema?

Agradecemos de antemano cualquier ayuda que nos podáis ofrecer.

Saludos cordiales,

Oscar

Product version: IRIS 2021.1
$ZV: IRIS for UNIX (Red Hat Enterprise Linux for x86-64) 2020.1.1 (Build 408U) Sun Mar 21 2021 22:21:14 EDT
Comentarios (6)3
Inicie sesión o regístrese para continuar

Hola @Oscar Tarriño Buen o ! Sospecho que el problema venga porque al definir el tipo estructurado de datos en el nuevo esquema estáis obligando a que todos los obx.5 cumplan con esa estructura de datos, pero en vuestro ejemplo tenéis en el segundo obx un valor simple que no encaja en el nuevo tipo. En el caso de usar el esquema que habéis definido el primer valor "115" debiera ir en un campo CE.1

Hola @Luis Angel Pérez Ramos,

yo también pensé que el problema podría venir si uno de los segmentos obx.5 venía sólo con el valor y no con la estructura. Y probé de enviar sólo un segmento OBX.5  con la estructura correcta :

<OBX.5 LongName="Observation Value">
    <CE.1>UNAF</CE.1>
    <CE.2>Ulleres nasals d alt flux</CE.2>
</OBX.5>

Pero tengo el mismo error.

En mi pregunta, añadí el otro obx.5 para indicar que podían venir de los dos tipos y por eso tambien comentó que a lo mejor cuztomizar el ORU_RO1 aunque funcione tampoco me resolverá el problema.

Muchas gracias 

He realizado la prueba cambiando el esquema cómo me has comentado pero tengo el mismo error:

<MSH.12>
    <VID.1>GACELA.2.5.v2</VID.1>
</MSH.12>

y me da el error:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:s="http://www.w3.org/2001/XMLSchema">
   <SOAP-ENV:Body>
      <SOAP-ENV:Fault>
         <faultcode>SOAP-ENV:Server</faultcode>
         <faultstring>Server Application Error</faultstring>
         <detail>
            <Exception>ERROR #6301: SAX XML Parser Error: &lt;INVALID OREF>zendElement+21^ITB.HL7.Format.HL7XMLv2.1 			if (..%SegObj.GetValueAt(..%FieldPath)="") { while processing Anonymous Stream at line 3 offset 25</Exception>
         </detail>
      </SOAP-ENV:Fault>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Muchas gracias.

Hola Óscar,

Veo que usas llamadas a ITB.HL7.Util.Convert así que parece que estás utilizando Healthcare HL7 XML para la conversión de HL7 entre formato XML y pipes.

Creo que lo que necesitas es poder trabajar con los campos OBX.5 con un tipo de datos dinámico (normalmente el que se indica en OBX.2).

En Healthcare HL7 XML tienes explicado como implementar esa opción en Segment fields with Dynamic Data Structures.