文章 sun yao · 一月 4, 2023 6m read

解析BP中的XData获取Switch-Case-Call Xml节点信息

一、目的 因使用BP中Switch分支来区分不同节点(即接口)及流向,个人已知方法中无相关统计功能能直接获取各分支节点及流向BO(调用第三方系统)信息,且当前使用系统未封装相关模块供查询,故通过解析BP中XData的方式获取Switch-Case节点及Call调用相关信息

二、解析类中的XData数据

/// 解析类中的XData内容
/// SELECT * FROM %Dictionary.CompiledXData
/// xdataID XData表ID
/// d ##class(BOE.TEST.ClassAnalysis).XDataAnalysisTextReader("JHIP.SM.BP.View||BPL")
ClassMethod XDataAnalysisTextReader(xdataID As %String) As %Status
{
   //获取XData流
   s compiledXdata=##class(%Dictionary.CompiledXData).%OpenId(xdataID)
   s tStream=compiledXdata.Data
   If '$IsObject(tStream) s tSC=%objlasterror q
   
   //逐行读取xml
   s status=##class(%XML.TextReader).ParseStream(tStream,.textreader)
   if $$$ISERR(status) do $System.Status.DisplayError(status) q
   s conditionMsgCode="sy"
   while textreader.Read()
   {
	   //节点类型为element
       if (textreader.NodeType="element"){
	       //节点路径中包含/switch/case且case为结尾
	       if ((textreader.Path["/switch/case")&&(textreader.Path'["/switch/case/")){ //接口switch case解析接口 element
	       		//节点有属性
	       		if (textreader.HasAttributes){
		       		//移动到对应的属性节点Attribute
		       		if (textreader.MoveToAttributeName("condition")){
			       		s condition=textreader.Value
			       		s len=$l(condition,"||")
			       		for i=1:1:len{
				       		s tepCondition=$p(condition,"||",i)
				       		if (tepCondition["'="){
					       		s conditionMsgCode=$tr($p(tepCondition,"action",2),"""")
					       		if (conditionMsgCode=""){
						       		s conditionMsgCode=$tr($p(tepCondition,"OriginalDocId",2),"""")
					       		}
				       		}else{
				       			s conditionMsgCode=$tr($p(tepCondition,"=",2),"""")
				       		}
			       		}
			       		//b:conditionMsgCode=""
			       		s conditionMsgCode=$tr(conditionMsgCode,"()")
			       		if (conditionMsgCode=""){ //如果未定义分支条件,则设置为类名
				       		s conditionMsgCode=xdataID
			       		}
			       		//移动到下一个节点Node
			       		d textreader.Read()
			       		if (textreader.HasAttributes){
				       		if (textreader.MoveToAttributeName("name")){
					       		s name=textreader.Value
			       				s ^sy(conditionMsgCode)=name
				       		}
			       		}else{
				       		s ^sy(conditionMsgCode)=""
			       		}
		       		}
	       		}		
	       }
	       if ((textreader.Path["/switch/case/")&&(textreader.Path["/call")&&(textreader.Path'["/call/")){ //接口switch case call解析接口 element
	       		s callName="",callTarget=""
	       		if (textreader.HasAttributes){
		       		if (textreader.MoveToAttributeName("name")){
			       		s callName=textreader.Value
			       		s ^sy(conditionMsgCode,"callName",callName)=callName
		       		}
		       		if (textreader.MoveToAttributeName("target")){
			       		s callTarget=textreader.Value
			       		s ^sy(conditionMsgCode,"callTarget",callTarget)=callTarget
		       		}
	       		}		
	       }
       }
   }
}

XData解析测试

  • 测试用BP结构
Class JHIP.SM.BP.View Extends Ens.BusinessProcessBPL
{

/// BPL Definition
XData BPL [ XMLNamespace = "http://www.intersystems.com/bpl" ]
{
<process language='objectscript' request='Ens.Request' response='Ens.Response' height='2000' width='2015' >
<context>
<property name='sqltype' type='%String' initialexpression='"Query"' instantiate='0' >
<parameters>
<parameter name='MAXLEN'  value='50' />
</parameters>
</property>
<property name='sqlstatement' type='%String' instantiate='0' >
<parameters>
<parameter name='MAXLEN'  value='1000' />
</parameters>
</property>
<property name='tablename' type='%String' instantiate='0' >
<parameters>
<parameter name='MAXLEN'  value='50' />
</parameters>
</property>
<property name='sql' type='JHIPLIB.SQL.MSG.SQLMessage' instantiate='0' />
<property name='sqlparameter' type='%String' instantiate='0' >
<parameters>
<parameter name='MAXLEN'  value='50' />
</parameters>
</property>
</context>
<sequence xend='200' yend='600' >
<switch name='判断接口' xpos='200' ypos='250' xend='200' yend='500' >
<case condition='request.OriginalDocId="JH1301"' >
<sequence name='JH1301手麻测试' xpos='335' ypos='400' xend='200' yend='850' >
<assign name="设定查询参数" property="context.sqlparameter" value="request.GetValueAt(&quot;/REQUEST/KSBM&quot;)" action="set" xpos='200' ypos='250' >
<annotation><![CDATA[设定查询参数]]></annotation>
</assign>
<assign name="设定SQL脚本" property="context.sqlstatement" value="&quot;select * from whkx_oas_operation_dept&quot;" action="set" xpos='200' ypos='350' >
<annotation><![CDATA[设定SQL脚本]]></annotation>
</assign>
<assign name="设定数据库表名" property="context.tablename" value="&quot;whkx_oas_operation_dept&quot;" action="set" xpos='200' ypos='450' >
<annotation><![CDATA[设定数据库表名]]></annotation>
</assign>
<assign name="设定SQL类型" property="context.sqltype" value="&quot;Query&quot;" action="set" xpos='200' ypos='550' >
<annotation><![CDATA[有QUERY、INSERT、UPDATE、DELETE4种]]></annotation>
</assign>
<code name='拼接sql脚本生成xml消息' xpos='200' ypos='650' >
<annotation><![CDATA[拼接sql脚本生成xml消息]]></annotation>
<![CDATA[ 
 Set sqlreq=##class(JHIPLIB.SQL.MSG.SQLMessage).%New()
 Set context.sqlstatement=$replace(context.sqlstatement,"?",context.sqlparameter)
 
 Set sqlreq.sqlreq=context.sqlstatement
 Set sqlreq.sqltype=context.sqltype
 Set sqlreq.tablename=context.tablename
 
 Set context.sql=sqlreq
 ]]>
</code>
<call name='查询手麻视图服务1' target='测试服务BO' async='0' xpos='200' ypos='750' >
<annotation><![CDATA[查询手麻视图服务1]]></annotation>
<request type='Ens.Request' >
<assign property="callrequest" value="context.sql" action="set" />
</request>
<response type='Ens.Response' >
<assign property="response" value="callresponse" action="set" />
</response>
</call>
</sequence>
</case>
<case condition='request.OriginalDocId="JH1302"' >
<sequence name='his测试' xpos='605' ypos='400' xend='200' yend='850' >
<assign name="设定查询参数" property="context.sqlparameter" value="request.GetValueAt(&quot;/REQUEST/KSBM&quot;)" action="set" xpos='200' ypos='250' >
<annotation><![CDATA[设定查询参数]]></annotation>
</assign>
<assign name="设定SQL脚本" property="context.sqlstatement" value="&quot;SELECT DISTINCT ORDERED_EMP_CODE  FROM core_his50.HIS_DEPT_INCOME_MONTH_I_H080 WHERE  HIS_PATIENT_ID = 'ZY010000000039'&quot;" action="set" xpos='200' ypos='350' >
<annotation><![CDATA[设定SQL脚本]]></annotation>
</assign>
<assign name="设定数据库表名" property="context.tablename" value="&quot;HIS_DEPT_INCOME_MONTH_I_H080&quot;" action="set" xpos='200' ypos='450' >
<annotation><![CDATA[设定数据库表名]]></annotation>
</assign>
<assign name="设定SQL类型" property="context.sqltype" value="&quot;Query&quot;" action="set" xpos='200' ypos='550' >
<annotation><![CDATA[有QUERY、INSERT、UPDATE、DELETE4种]]></annotation>
</assign>
<code name='拼接sql脚本生成xml消息' xpos='200' ypos='650' >
<annotation><![CDATA[拼接sql脚本生成xml消息]]></annotation>
<![CDATA[ 
 Set sqlreq=##class(JHIPLIB.SQL.MSG.SQLMessage).%New()
 Set context.sqlstatement=$replace(context.sqlstatement,"?",context.sqlparameter)
 
 Set sqlreq.sqlreq=context.sqlstatement
 Set sqlreq.sqltype=context.sqltype
 Set sqlreq.tablename=context.tablename
 
 Set context.sql=sqlreq
 ]]>
</code>
<call name='查询手麻视图服务2' target='his测试BO' async='0' xpos='200' ypos='750' >
<annotation><![CDATA[查询手麻视图服务1]]></annotation>
<request type='Ens.Request' >
<assign property="callrequest" value="context.sql" action="set" />
</request>
<response type='Ens.Response' >
<assign property="response" value="callresponse" action="set" />
</response>
</call>
</sequence>
</case>
<case condition='request.OriginalDocId="JH0000"' >
<sequence name='JH000001' xpos='875' ypos='400' xend='200' yend='850' >
<assign name="设定查询参数" property="context.sqlparameter" value="request.GetValueAt(&quot;/REQUEST/KSBM&quot;)" action="set" xpos='200' ypos='250' >
<annotation><![CDATA[设定查询参数]]></annotation>
</assign>
<assign name="设定SQL脚本" property="context.sqlstatement" value="&quot;SELECT * FROM core_his50.His_dict_dept_H080&quot;" action="set" xpos='200' ypos='350' >
<annotation><![CDATA[设定SQL脚本]]></annotation>
</assign>
<assign name="设定数据库表名" property="context.tablename" value="&quot;core_his50.His_dict_dept_H080&quot;" action="set" xpos='200' ypos='450' >
<annotation><![CDATA[设定数据库表名]]></annotation>
</assign>
<assign name="设定SQL类型" property="context.sqltype" value="&quot;Query&quot;" action="set" xpos='200' ypos='550' >
<annotation><![CDATA[有QUERY、INSERT、UPDATE、DELETE4种]]></annotation>
</assign>
<code name='拼接sql脚本生成xml消息' xpos='200' ypos='650' >
<annotation><![CDATA[拼接sql脚本生成xml消息]]></annotation>
<![CDATA[ 
 Set sqlreq=##class(JHIPLIB.SQL.MSG.SQLMessage).%New()
 Set context.sqlstatement=$replace(context.sqlstatement,"?",context.sqlparameter)
 
 Set sqlreq.sqlreq=context.sqlstatement
 Set sqlreq.sqltype=context.sqltype
 Set sqlreq.tablename=context.tablename
 
 Set context.sql=sqlreq
 ]]>
</code>
<call name='查询手麻视图服务3' target='his测试BO' async='0' xpos='200' ypos='750' >
<annotation><![CDATA[查询手麻视图服务1]]></annotation>
<request type='Ens.Request' >
<assign property="callrequest" value="context.sql" action="set" />
</request>
<response type='Ens.Response' >
<assign property="response" value="callresponse" action="set" />
</response>
</call>
</sequence>
</case>
<case condition='request.OriginalDocId="JH0000"' >
<sequence name='JH000002' xpos='1145' ypos='400' xend='200' yend='850' disabled="true">
<assign name="设定查询参数" property="context.sqlparameter" value="request.GetValueAt(&quot;/REQUEST/KSBM&quot;)" action="set" xpos='200' ypos='250' >
<annotation><![CDATA[设定查询参数]]></annotation>
</assign>
<assign name="设定SQL脚本" property="context.sqlstatement" value="&quot;SELECT * FROM His_dict_dept_H080&quot;" action="set" xpos='200' ypos='350' >
<annotation><![CDATA[设定SQL脚本]]></annotation>
</assign>
<assign name="设定数据库表名" property="context.tablename" value="&quot;dhis.His_dict_dept_H080&quot;" action="set" xpos='200' ypos='450' >
<annotation><![CDATA[设定数据库表名]]></annotation>
</assign>
<assign name="设定SQL类型" property="context.sqltype" value="&quot;Query&quot;" action="set" xpos='200' ypos='550' >
<annotation><![CDATA[有QUERY、INSERT、UPDATE、DELETE4种]]></annotation>
</assign>
<code name='拼接sql脚本生成xml消息' xpos='200' ypos='650' >
<annotation><![CDATA[拼接sql脚本生成xml消息]]></annotation>
<![CDATA[ 
 Set sqlreq=##class(JHIPLIB.SQL.MSG.SQLMessage).%New()
 Set context.sqlstatement=$replace(context.sqlstatement,"?",context.sqlparameter)
 
 Set sqlreq.sqlreq=context.sqlstatement
 Set sqlreq.sqltype=context.sqltype
 Set sqlreq.tablename=context.tablename
 
 Set context.sql=sqlreq
 ]]>
</code>
<call name='查询手麻视图服务1' target='测试服务BO' async='1' xpos='200' ypos='750' >
<annotation><![CDATA[查询手麻视图服务1]]></annotation>
<request type='Ens.Request' >
<assign property="callrequest" value="context.sql" action="set" />
</request>
<response type='Ens.Response' >
<assign property="response" value="callresponse" action="set" />
</response>
</call>
</sequence>
</case>
<case condition='request.action="JH1401"' >
<call name='HTTP测试' target='测试HTTP样例BO' async='1' xpos='1415' ypos='400' >
<request type='Ens.Request' >
<assign property="callrequest" value="request" action="set" />
</request>
<response type='Ens.Response' >
<assign property="response" value="callresponse" action="set" />
</response>
</call>
</case>
<default name='default' />
</switch>
</sequence>
</process>
}

Storage Default
{
<Type>%Storage.Persistent</Type>
}

}
  • 测试结果

image

  • 参考 image

%XML.TextReader逐个节点地读取和解析文档。 %XML.XPATH.document使用引用文档中特定节点的XPATH表达式来获取数据。 [XMLTools参考链接](%3Cdiv align="justify" style="min-height: 13pt; "%3E%3Ca href="https://docs.intersystems.com/irisforhealth20222/csp/docbook/DocBook.UI.Page.cls?KEY=GXML_intro"%3E%3Cfont face="Calibri" color="#0000ff" size="2"%3E%3Cspan dir="ltr" style=" font-size:10.5pt"%3E%3Cu%3Ehttps://docs.intersystems.com/irisforhealth20222/csp/docbook/DocBook.UI.Page.cls?KEY=GXML_intro%3C/u%3E%3C/span%3E%3C/font%3E%3C/a%3E%3C/div%3E)

三、获取工程下所有BP类型类文件

/// 获取工程下所有BP类型类文件
/// SELECT * FROM %Studio.Project
/// package:包名称
/// w ##class(BOE.TEST.ClassAnalysis).GetClassNameByPackage()			
ClassMethod GetClassNameByPackage(package As %String = "") As %String
{
				
	s $zt="Exception"
	k ^sy //初始化,Global用于存储分支节点相关信息			
	s project=""
	for{
		s project=$o(^oddPROJECT(project))
		q:project=""
		s rs=##class(%Library.ResultSet).%New("%Studio.Project:ProjectItemsList")
		//s columns=rs.GetColumnCount()
		s sc=rs.Execute(project)
		while(rs.Next()){
			s i=0			
			s name=rs.GetData(2) //Item名称			
			s type=rs.GetData(3) //Item类型			
			s tempPackage=rs.GetData(5) //包名			
			if type="CLS"{ //类文件
				s theCompiledClassObj = ##Class(%Dictionary.CompiledClass).%OpenId(name)
				continue:theCompiledClassObj=""
				s primarySuper=theCompiledClassObj.PrimarySuper
				continue:primarySuper'["Ens.BusinessProcess" //只处理BP
				if (package'=""){
					for j=1:1:$L(name,"."){
						if $P(name,".",j)=package s i=1 //过滤包名
					}
					continue:i=0
				}
				s xDataId=""
				&sql(SELECT top 1 ID into :xDataId FROM %Dictionary.CompiledXData WHERE parent=:name)
				if (xDataId'=""){
					//解析XData
					d ##class(BOE.TEST.ClassAnalysis).XDataAnalysisTextReader(xDataId)
				}
			}
		}
	}
	q 1
Exception
	q "-1^"_$ze
}

查询已生成的分支节点数据

/// 查询接口目录
/// d ##class(%Results).RunQuery("BOE.TEST.ClassAnalysis","GetMsgInfo")
Query GetMsgInfo() As %Query(ROWSPEC = "msgCode,msgDesc,callName,callTarget") [ SqlProc ]
{
}

ClassMethod GetMsgInfoExecute(ByRef qHandle As %Binary) As %Status
{
 	s repid=$I(^CacheTemp)
 	s ind=1
 	s qHandle=$lb(0,repid,0)
	s msgCode=""
	for{
		s msgCode=$o(^sy(msgCode)) //接口编码
		q:msgCode=""
		s msgDesc=^sy(msgCode) //接口描述
		s callName="",callTarget=""
		for{
			s callName=$o(^sy(msgCode,"callName",callName))
			q:callName=""
			d OutRow
		}
		
	}
	
	Quit $$$OK

OutRow
	s Data=$lb(msgCode,msgDesc,callName,callTarget)
 	s ^CacheTemp(repid,ind)=Data
 	s ind=ind+1
	quit
}

ClassMethod GetMsgInfoFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = GetBISQCIndexDataExecute ]
{
	s AtEnd=$LIST(qHandle,1)
 	s repid=$LIST(qHandle,2)
 	s ind=$LIST(qHandle,3)
 	s ind=$o(^CacheTemp(repid,ind))
 	If ind="" {				// if there are no more rows, finish fetching
 		s AtEnd=1
 		s Row=""
 	}
 	Else      {			
 		s Row=^CacheTemp(repid,ind)
 	}
 	s qHandle=$lb(AtEnd,repid,ind)
	Quit $$$OK
}

ClassMethod GetMsgInfoClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = GetBISQCIndexDataExecute ]
{
	s repid=$LIST(qHandle,2)
 	Kill ^CacheTemp(repid)
    Quit $$$OK
}

工程分支节点生成及查询测试

  • 测试结果 image

  • 通过存储过程查询

call BOE_TEST.ClassAnalysis_GetMsgInfo()

image