我正在一个函数中创建xml结构的基本轮廓,然后将用于创建xml内部/数据部分的数据传递到另一个函数中。如何使用Coldfusion将(或附加)添加到不同的xml结构中。我也不是很熟悉使用xml或Coldfusion,并且cf docs并没有真正帮助太多。
这是我正在做的事情的简化版本,但它说明了这一点:
<cffunction name="getSomeXML" access="private">
<cfargument name="qryToGetData" type="query">
<cfset var LOCAL = StructNew()>
<cfsavecontent variable="LOCAL.XML"><?xml version="1.0" encoding="utf-8"?>
<Types>
<cfoutput query="qryToGetData">
<A><![CDATA[#xmlFormat(qryToGetData.aType)#]]></A>
<B><![CDATA[#xmlFormat(qryToGetData.bType)#]]></B>
<C><![CDATA[#xmlFormat(qryToGetData.cType)#]]></C>
<D><![CDATA[#xmlFormat(qryToGetData.dType)#]]></D>
</cfoutput>
</Types>
</cfsavecontent>
<cfset LOCAL = createInnerXML(LOCAL)> <!--- Call Function to Make Inner XML --->
<cfreturn LOCAL> <!--- Return Complete XML --->
</cffunction>
<cffunction name="innerXML" access="private">
<cfargument name="arguments" hint="arguments">
<cfset var innerXMLSRT = StructNew()>
<cfsavecontent variable="innerXMLSRT.XML"><?xml version="1.0" encoding="utf-8"?>
<Data>
<cfoutput>
#arguments.xmlFeed#
</cfoutput>
</Data>
</cfsavecontent>
<cfreturn innerXMLSRT> <!--- Return Complete XML --->
</cffunction>
<!--- OUTPUT: --->
<Types>
<Data>
<A></A>
<B></B>
<C></C>
<D></D>
</Data>
</Types>
答案 0 :(得分:3)
实际上,您想将一个XML文档插入某个位置的另一个XML文档中。
正确的 规范方法是以下步骤序列:
由于XML节点不能在不同的DOM树之间自由移动,因此这种相当复杂的方法是必需的。每个节点都有一个“所有者文档”,该文档在节点的生存期内无法更改。创建物理副本是将节点放入不同文档的唯一方法。
但是,在ColdFusion中增加了复杂性。它支持使用XML文档的一些非常方便的语法。 XML对象的行为既类似于数组又类似于结构,并且可以使用相关功能,并且您可以在代码中直接使用像xml.Data.Foo.XmlAttributes.Bar
这样的点路径来读取例如属性值。这种便利的程度是有代价的-ColdFusion将XML节点包装到抽象层中,从而隐藏了底层的实际节点对象,这总体上使访问节点导入等所需的DOM方法变得困难。
在深入研究了ColdFusion的XML内部之后,我想到了一个优雅的函数:
<cffunction name="XmlAppend" returntype="void" output="no">
<cfargument name="target" type="xml" required="yes">
<cfargument name="source" type="array" required="yes">
<cfset var sNode = "">
<cfloop array="#source#" index="sNode">
<cfset target.appendChild(
target.getOwnerDocument().importNode(sNode.cloneNode(true), true)
)>
</cfloop>
</cffunction>
此功能可以将任意数量的source
节点复制到target
节点。由于该函数就地修改了目标节点,因此没有返回值。
关键部分是调用.cloneNode()
。 .importNode()
函数无法处理sNode
实际上是的ColdFusion XML包装对象(类:coldfusion.xml.XmlNodeList
,包装org.apache.xerces.dom.DeferredElementNSImpl
和表亲)。
(我发现)没有办法从包装中获取基础XML节点对象,除非通过克隆它。因此,此步骤很浪费,但没有其他事情.importNode()
发挥作用。
有了这些XML文档,
<cfxml variable="dataXml">
<Types>
<a>aaaa</a>
<a>bbbb</a>
</Types>
</cfxml>
<cfxml variable="wrapperXml">
<Data>
<some>exsting node</some>
</Data>
</cfxml>
呼叫将像这样工作:
<!-- all children of /Types into the /Data node -->
<cfset XmlAppend(wrapperXml.Data, dataXml.Types.XmlChildren)>
<!-- same thing, but with XPath and explicit argument names -->
<cfset XmlAppend(
target: XmlSearch(wrapperXml, '/Data')[1],
source: XmlSearch(dataXml, '/Types/*')
)>
并导致以下结果:
<Data>
<some>exsting node</some>
<a>aaaa</a>
<a>bbbb</a>
</Data>
此函数使用大量未公开的API。我已经从CF 7到CF 2016对其进行了测试,并且可以在所有这些版本中使用,但是当您将其放入生产代码中,而以后对ColdFusion的更新会破坏它,或者当它不起作用时,您仍然会独自一人。完全无法在Railo等第三方实现中使用。使用后果自负。
为了完整起见,请Ben Nadel has also implemented an XmlAppend
function。
仅使用官方支持的API,在ColdFusion中无法以 nice 的方式完成此任务。
以下方法仅使用官方API,但在XML上使用正则表达式和字符串插值根本不是我所说的clean:
<!-- remove XML declaration if present -->
<cfset var dataXmlStr = REReplace(ToString(dataXml), '^<\?xml[^>]*>', '')>
<cfxml variable="wrapperXml">
<Data><cfoutput>#dataXmlStr#</cfoutput></Data>
</cfxml>
删除XML声明至关重要,因为ColdFusion总是在我们对XML对象调用ToString()
时添加它,但是在XML文档中的任何地方(除了在开始时只出现一次)都是不合法的。忘记删除XML声明将导致<cfxml>
中的解析器错误。
请注意,<cfxml>
与<cfsavecontent>
实际上是相同的-它从其主体创建一个字符串,但作为最后一步,它将该字符串解析为XML文档。
我已经对其进行了测量,并且importNode
/ appendNode
方法似乎比字符串插值方法更快。多少取决于实际任务。
答案 1 :(得分:1)
已更新02.07.18
好。在Ben Nadel的AppendXml()函数的基础上,我为您的需求创建了一个自定义函数,该函数将使用纯XML方法追加到现有节点上。
功能:
如果您的列名不同,只需将查询列引用更改为新的列即可。
<cffunction name="AppendXml" returntype="any" output="no" access="private">
<!---arguments--->
<cfargument name="query" type="query" required="yes" hint="I output the CDATA">
<cfargument name="xml" type="any" required="yes" hint="I am the orginal XML text string that requires appending">
<cfargument name="outputString" type="boolean" required="no" default="true" hint="I output whether the XML is in object or string format">
<!---local variables--->
<cfset var local = StructNew()>
<!---logic--->
<cfset local.xml = REReplaceNoCase(ToString(arguments.xml),"<\?[^>]*>","")>
<cfif IsXml(local.xml)>
<cfset local.xml = XmlParse(local.xml)>
<cfset ArrayAppend(local.xml.Data.XmlChildren,XmlElemNew(local.xml,"Types"))>
<cfloop query="arguments.query">
<cfset ArrayAppend(local.xml.Data.Types[ArrayLen(local.xml.Data.XmlChildren)].XmlChildren,XmlElemNew(local.xml,"A"))>
<cfset local.xml.Data.Types[ArrayLen(local.xml.Data.XmlChildren)]["A"].XmlCData = xmlFormat("aType")>
<cfset ArrayAppend(local.xml.Data.Types[ArrayLen(local.xml.Data.XmlChildren)].XmlChildren,XmlElemNew(local.xml,"B"))>
<cfset local.xml.Data.Types[ArrayLen(local.xml.Data.XmlChildren)]["B"].XmlCData = xmlFormat("bType")>
<cfset ArrayAppend(local.xml.Data.Types[ArrayLen(local.xml.Data.XmlChildren)].XmlChildren,XmlElemNew(local.xml,"C"))>
<cfset local.xml.Data.Types[ArrayLen(local.xml.Data.XmlChildren)]["C"].XmlCData = xmlFormat("cType")>
<cfset ArrayAppend(local.xml.Data.Types[ArrayLen(local.xml.Data.XmlChildren)].XmlChildren,XmlElemNew(local.xml,"D"))>
<cfset local.xml.Data.Types[ArrayLen(local.xml.Data.XmlChildren)]["D"].XmlCData = xmlFormat("dType")>
</cfloop>
<cfif arguments.outputString>
<cfset local.xml = ToString(local.xml)>
</cfif>
<cfelse>
<cfset local.xml = arguments.xml>
</cfif>
<cfreturn local.xml>
</cffunction>
<cfset query = QueryNew("aType,bType,cType,dType")>
<cfset QueryAddRow(query)>
<cfset QuerySetCell(query,"aType","small")>
<cfset QuerySetCell(query,"bType","medium")>
<cfset QuerySetCell(query,"cType","large")>
<cfset QuerySetCell(query,"dType","extra large")>
<cfsavecontent variable="xml">
<?xml version="1.0" encoding="utf-8"?>
<Data>
<Types>
<A><![CDATA[small]]></A>
<B><![CDATA[medium]]></B>
<C><![CDATA[large]]></C>
<D><![CDATA[extralarge]]></D>
</Types>
<Types>
<A><![CDATA[small]]></A>
<B><![CDATA[medium]]></B>
<C><![CDATA[large]]></C>
<D><![CDATA[extralarge]]></D>
</Types>
</Data>
</cfsavecontent>
<cfset AppendXml = AppendXml(query=query,xml=xml,outputString=false)>
<cfdump var="#AppendXml#" />