Get the XML Subtree from XPATH DOM Resultobject
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
Code:
{
#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
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>
tParams should be an array:
set tParms("tExperssion") = tExperssion set tParms("tIndex") = tIndex
and this line:
should maybe be:
<xsl:value-of select="$tExperssion"/>
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
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:
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
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.
{
#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/Transform" version="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
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>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