Get the XML Subtree from XPATH DOM Resultobject

Primary tabs

Hello,

How to get XML subtree from XPATH.DOMResult object as Stream or CacheString.

 

Here is my classmethod. My system receives XML message which has nested structures. The requirement is to send one message at a time to the destination. I have created the XPATH document and used EvaluateExpression method and which returns XML.XPATH.Result of type DOM. I am able to read the dom and get value of the element and its text. But I am looking to send back the subtree.

Code:

ClassMethod GetNode(tData As %CacheString, tExperssion As %String, tIndex As %Integer) As %CacheString
{
#dim tNodeText As %CacheString

    // Create an XPATH Document instance from the stream of XML
    Set tSC=##class(%XML.XPATH.Document).CreateFromString(tData,.tDocument)
if tDocument=$$$NULLOREF $$$ThrowStatus($$$ERROR($$$GeneralError,"Could not create XPath Document from Stream"))
zw tDocument
//Evaluate the XPATH expression
    Set tSC=tDocument.EvaluateExpression("//DIAGNOSES",tExperssion_"["_tIndex_"]",.tResults)
    zw tResults
if tResults=$$$NULLOREF $$$ThrowStatus($$$ERROR($$$GeneralError,"XPATH Expression evaluation failed."))


// Retrieve the XPATH Result
//$$$TRACE("XML Type:"_tResults.GetAt(1).Type)
set tResult=tResults.GetAt(1)
zw tResult
//do tResult.Read()
         While tResult.Read()
            {   
                If tResult.NodeType="element"
                {
                    Write !,tResult.NodeType,": ",tResult.Name  
                    
                    If tResult.HasAttributes {
                        For tJ=1:1:tResult.AttributeCount
                        {
                            Do tResult.MoveToAttributeIndex(tJ)
                            Write !,?9,tResult.NodeType,": ",tResult.Name,?25," Value: ",tResult.Value
                        }
                    }
                else {
                    
                    Write !,tResult.NodeType," : ",tResult.Name," Value: "
                
                    // Value can be a stream if result is greater than 32k in length
                    Set tValue=tResult.Value
                
                    If $IsObject(tValue){
                        Write ! Do tValue.OutputToDevice()
                    else {
                        Write tValue
                    }
                }
                Write !
            }

zw tResult
set tNodeText = tResult.ValueGet()
$$$TRACE("Number of Node of "_tExperssion_":"_tNodeText)

//return the Node Text
Quit tNodeText
}

test Command:

set tData="<HHSOS><DIAGNOSES ><DIAGNOSIS_DATA><DIAGNOSIS_DATA_GUID>3762875</DIAGNOSIS_DATA_GUID><DIAGNOSIS_DATA_GUID>37628752</DIAGNOSIS_DATA_GUID></DIAGNOSIS_DATA><DIAGNOSIS_DATA></DIAGNOSIS_DATA><DIAGNOSIS_DATA></DIAGNOSIS_DATA><DIAGNOSIS_DATA_GUID>37628753</DIAGNOSIS_DATA_GUID></DIAGNOSES></HHSOS>"

set t=##class(ISG.KAH.Devero.Utils.ToHorizonFunctions).GetNode(tData,"DIAGNOSIS_DATA",1)

 

Thank you

Raghu

 

 

 

 

 

  • 0
  • 0
  • 722
  • 6
  • 2

Answers

I see $$$TRACE statements in your code, which leads me to believe you are using Ensemble.

What you're trying to do is much easier with EnsLib.EDI.XML.Document, and there are adapters supporting the use of this message class.

Here's an example:

ENSEMBLE>set msg = ##class(EnsLib.EDI.XML.Document).ImportFromString(tData)

ENSEMBLE>w msg.GetValueAt("/")
<HHSOS><DIAGNOSES><DIAGNOSIS_DATA><DIAGNOSIS_DATA_GUID>3762875</DIAGNOSIS_DATA_GUID><DIAGNOSIS_DATA_GUID>37628752</DIAGNOSIS_DATA_GUID></DIAGNOSIS_DATA><DIAGNOSIS_DATA/><DIAGNOSIS_DATA/><DIAGNOSIS_DATA_GUID>37628753</DIAGNOSIS_DATA_GUID></DIAGNOSES></HHSOS>

ENSEMBLE>w msg.GetValueAt("/HHSOS/DIAGNOSES")
<DIAGNOSIS_DATA><DIAGNOSIS_DATA_GUID>3762875</DIAGNOSIS_DATA_GUID><DIAGNOSIS_DATA_GUID>37628752</DIAGNOSIS_DATA_GUID></DIAGNOSIS_DATA><DIAGNOSIS_DATA/><DIAGNOSIS_DATA/><DIAGNOSIS_DATA_GUID>37628753</DIAGNOSIS_DATA_GUID>

ENSEMBLE>w msg.GetValueAt("/HHSOS/DIAGNOSES/DIAGNOSIS_DATA[*]")
3

ENSEMBLE>w msg.GetValueAt("/HHSOS/DIAGNOSES/DIAGNOSIS_DATA[1]")
<DIAGNOSIS_DATA_GUID>3762875</DIAGNOSIS_DATA_GUID><DIAGNOSIS_DATA_GUID>37628752</DIAGNOSIS_DATA_GUID>

ENSEMBLE>w msg.GetValueAt("/HHSOS/DIAGNOSES/DIAGNOSIS_DATA[2]")

ENSEMBLE>w msg.GetValueAt("/HHSOS/DIAGNOSES/DIAGNOSIS_DATA[1]/DIAGNOSIS_DATA_GUID[*]")
2

ENSEMBLE>w msg.GetValueAt("/HHSOS/DIAGNOSES/DIAGNOSIS_DATA[1]/DIAGNOSIS_DATA_GUID[1]")
3762875

ENSEMBLE>w msg.GetValueAt("/HHSOS/DIAGNOSES/DIAGNOSIS_DATA[1]/DIAGNOSIS_DATA_GUID[2]")
37628752
 

These are called DOM-style paths as opposed to VDoc paths.  Here's the documentation:

http://docs.intersystems.com/ens20161/csp/docbook/DocBook.UI.Page.cls?KE... 

Thank you, Brendan. This is much simpler and clean way of doing. Appreciate your help.

 

Raghu

Hi Raghu.

I don't know about XPath, but maybe using XSLT might help you here:

Class Sample.XSLTransform [ Abstract ]
{

ClassMethod test()
{
    set tXML= ##class(%GlobalCharacterStream).%New()
    do tXML.Write("<HHSOS><DIAGNOSES><DIAGNOSIS_DATA><DIAGNOSIS_DATA_GUID>3762875</DIAGNOSIS_DATA_GUID><DIAGNOSIS_DATA_GUID>37628752</DIAGNOSIS_DATA_GUID></DIAGNOSIS_DATA><DIAGNOSIS_DATA></DIAGNOSIS_DATA><DIAGNOSIS_DATA></DIAGNOSIS_DATA><DIAGNOSIS_DATA_GUID>37628753</DIAGNOSIS_DATA_GUID></DIAGNOSES></HHSOS>")

    set tXSL=##class(%Dictionary.CompiledXData).%OpenId(..%ClassName(1)_"||ExampleXSL").Data

    set tSC=##class(%XML.XSLT.Transformer).TransformStream(tXML,tXSL,.tOutput)
    zwrite tSC
    set tSC=tOutput.OutputToDevice()
}

XData ExampleXSL
{
<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
    <xsl:copy-of select="//DIAGNOSIS_DATA_GUID[1]"/>
</xsl:template>
</xsl:stylesheet>
}

}

SAMPLES>d ##class(Sample.XSLTransform).test()
tSC=1
<?xml version="1.0" encoding="UTF-8"?><DIAGNOSIS_DATA_GUID>3762875</DIAGNOSIS_DATA_GUID><DIAGNOSIS_DATA_GUID>37628753</DIAGNOSIS_DATA_GUID>

Thank you, Alexander. I went tried XSLT approach, it works when I have hard code the query in the xslt template but when I send the query has params I am not getting any result back. Do you have an example or code snippet that sends the query as params to the xslt?

 

Here is what I have tried.

 

ClassMethod GetNodeXSL(tData As %CacheString, tExperssion As %String, tIndex As %Integer) As %CacheString
{

    #dim tParms As %ArrayOfDataTypes
    
    set tParms = ##class(%ArrayOfDataTypes).%New()
    do tParms.SetAt(tExperssion,1)
    do tParms.SetAt(tIndex,2)
    set tXSL=##class(%Dictionary.CompiledXData).%OpenId(..%ClassName(1)_"||GetNodeXSL").Data
    set tSC=##class(%XML.XSLT.Transformer).TransformStream(tData,tXSL,.tOutput,,.tParms)
    zwrite tSC
    set tSC=tOutput.OutputToDevice()
    Quit tOutput
}

XData GetNodeXSL
{
<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transformversion="1.0">
<xsl:template match="/">
<xsl:param name="tExperssion"/>
<xsl:param name="tIndex" />
    <xsl:copy-of select="$tExperssion"/>
    </xsl:template>
  </xsl:stylesheet>
}

 

Test Command.

 

set t=##class(ISG.KAH.Devero.Utils.ToHorizonFunctions).GetNodeXSL(tData,"//DIAGNOSIS_DATA",1)

Thanks

Raghu

tParams should be an array:

set tParms("tExperssion") = tExperssion
set tParms("tIndex") = tIndex

and this line:

<xsl:copy-of select="$tExperssion"/>

should maybe be:

<xsl:value-of select="$tExperssion"/>

I have tried with value-of, still, returns the empty. I am thinking that the problem sending the values from classmethod to XDATA block.

 

Thanks

Here's working example:

Class Sample.XSLTransform [ Abstract ]
{

ClassMethod test(tData = "<HHSOS><DIAGNOSES><DIAGNOSIS_DATA><DIAGNOSIS_DATA_GUID>3762875</DIAGNOSIS_DATA_GUID><DIAGNOSIS_DATA_GUID>37628752</DIAGNOSIS_DATA_GUID></DIAGNOSIS_DATA><DIAGNOSIS_DATA></DIAGNOSIS_DATA><DIAGNOSIS_DATA></DIAGNOSIS_DATA><DIAGNOSIS_DATA_GUID>37628753</DIAGNOSIS_DATA_GUID></DIAGNOSES></HHSOS>", tSelect = "//DIAGNOSIS_DATA_GUID[1]", tXSL = "ExampleXSL")
{
    set tXML= ##class(%GlobalCharacterStream).%New()
    do tXML.Write(tData)
    set tXSL=##class(%Dictionary.CompiledXData).%OpenId($classname() _ "||" _ tXSL ).Data

    kill tParams
    set tParams("selectParam") = tSelect
    set tSC=##class(%XML.XSLT.Transformer).TransformStream(tXML,tXSL,.tOutput,,.tParams)
    zwrite tSC
    set tSC=tOutput.OutputToDevice()
}

XData ExampleXSL
{
<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="selectParam"/>
<xsl:template match="/">
    <xsl:copy-of select="$selectParam"/>
</xsl:template>
</xsl:stylesheet>
}

}

Example:

Do ##class(Sample.XSLTransform).test()
tSC=1
<?xml version="1.0" encoding="UTF-8"?><DIAGNOSIS_DATA_GUID>3762875</DIAGNOSIS_DATA_GUID><DIAGNOSIS_DATA_GUID>37628753</DIAGNOSIS_DATA_GUID>

Thank you,Eduard. The problem is I have declared the variable within the template and it should be outside. I got it working now.

 

just an oversight...

 

Thank you for your help again :-)

 

Raghu