如何解决Workday报告XSLT转JSON的序列处理错误?
解决XSLT JSON输出错误:无法处理多个项的序列
我基于Workday XML报告数据开发了通用XSLT转换程序(无硬编码列),设置output method="text"时程序可正常运行,但改为json时触发错误:Error executing XSLT at line 8 : JSON output method cannot handle sequences of two or more items,目标是生成符合预期格式的JSON数据。
XML数据
<wd:Report_Data xmlns:wd="urn:com.workday.report/EntryReason"> <wd:Report_Entry> <wd:EntryReasonRefID>ABC_123</wd:EntryReasonRefID> <wd:EntryReason>ABC_123 Reimbursement - Non Taxable</wd:EntryReason> <wd:CreatedMoment>2022-08-30T18:35:34.040-07:00</wd:CreatedMoment> <wd:LastFunctionallyUpdated>2022-09-01T12:37:48.427-07:00</wd:LastFunctionallyUpdated> </wd:Report_Entry> <wd:Report_Entry> <wd:EntryReasonRefID>ABC_456</wd:EntryReasonRefID> <wd:EntryReason>ABC_456 - Air Travel</wd:EntryReason> <wd:CreatedMoment>2022-08-30T18:35:34.370-07:00</wd:CreatedMoment> <wd:LastFunctionallyUpdated>2022-09-01T12:37:48.552-07:00</wd:LastFunctionallyUpdated> </wd:Report_Entry> <wd:Report_Entry> <wd:EntryReasonRefID>ABC_789</wd:EntryReasonRefID> <wd:EntryReason>ABC_789 - Car Rental</wd:EntryReason> <wd:CreatedMoment>2022-08-30T18:35:34.501-07:00</wd:CreatedMoment> <wd:LastFunctionallyUpdated>2022-09-01T12:37:48.672-07:00</wd:LastFunctionallyUpdated> </wd:Report_Entry> <wd:Report_Entry> <wd:EntryReasonRefID>ABC_999</wd:EntryReasonRefID> <wd:EntryReason>ABC_999 - Hotel</wd:EntryReason> <wd:EntryReason>ABC_999 - Rental</wd:EntryReason> <wd:CreatedMoment>2022-08-30T18:35:34.636-07:00</wd:CreatedMoment> <wd:LastFunctionallyUpdated>2022-09-01T12:37:48.779-07:00</wd:LastFunctionallyUpdated> </wd:Report_Entry> </wd:Report_Data>
原XSLT代码
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wd="urn:com.workday.report/EntryReason" version="3.0"> <xsl:output method="json" indent="yes"/> <xsl:template match="/wd:Report_Data"> <xsl:text> [
</xsl:text> <xsl:for-each select="wd:Report_Entry"> <xsl:text> {
</xsl:text> <xsl:for-each select="./*"> <xsl:text> </xsl:text> <xsl:choose> <xsl:when test="@wd:Descriptor"><xsl:value-of select="concat('"',local-name(),'":"',@wd:Descriptor,'"')"/></xsl:when> <xsl:otherwise><xsl:value-of select="concat('"',local-name(),'":"',.,'"')"/></xsl:otherwise> </xsl:choose> <xsl:if test="position() != last()"> <xsl:text>,
 </xsl:text> </xsl:if> </xsl:for-each> <xsl:text>
  }</xsl:text> <xsl:if test="position() != last()"> <xsl:text>,
</xsl:text> </xsl:if> </xsl:for-each> <xsl:text>
 ]</xsl:text> </xsl:template> <xsl:template match="node() | @*"/> </xsl:stylesheet>
错误详情
Error executing XSLT at line 8 : JSON output method cannot handle sequences of two or more items
预期输出
[ { "EntryReasonRefID": "ABC_123", "EntryReason": "ABC_123 Reimbursement - Non Taxable", "CreatedMoment": "2022-08-30T18:35:34.040-07:00", "LastFunctionallyUpdated": "2022-09-01T12:37:48.427-07:00" }, { "EntryReasonRefID": "ABC_456", "EntryReason": "ABC_456 - Air Travel", "CreatedMoment": "2022-08-30T18:35:34.370-07:00", "LastFunctionallyUpdated": "2022-09-01T12:37:48.552-07:00" }, { "EntryReasonRefID": "ABC_789", "EntryReason": "ABC_789 - Car Rental", "CreatedMoment": "2022-08-30T18:35:34.501-07:00", "LastFunctionallyUpdated": "2022-09-01T12:37:48.672-07:00" }, { "EntryReasonRefID": "ABC_999", "EntryReason": "ABC_999 - Hotel; ABC_999 - Rental", "CreatedMoment": "2022-08-30T18:35:34.636-07:00", "LastFunctionallyUpdated": "2022-09-01T12:37:48.779-07:00" } ]
(注:修正了原预期输出中EntryReasonRefID与EntryReason的对应错误,使其匹配XML数据逻辑)
解决方案
错误原因
当使用method="json"时,XSLT处理器要求输出单个JSON值节点(数组或对象),但原代码通过拼接多个文本节点生成JSON结构,导致输出的是多节点序列,不符合JSON输出方法的规范,因此触发错误。
修改后的XSLT代码
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wd="urn:com.workday.report/EntryReason" xmlns:map="http://www.w3.org/2005/xpath-functions/map" version="3.0"> <xsl:output method="json" indent="yes"/> <xsl:template match="/wd:Report_Data"> <!-- 构造JSON数组,每个元素为Report_Entry对应的对象 --> <xsl:sequence select="array{ for $entry in wd:Report_Entry return map:merge( for $group in $entry/* group by $name := local-name() return map { $name : if (count($group) > 1) then string-join($group, '; ') else string($group) } ) }"/> </xsl:template> <xsl:template match="node() | @*"/> </xsl:stylesheet>
代码说明
- 数组构造:用
array{}直接生成JSON数组,遍历每个wd:Report_Entry作为数组元素。 - 重复节点合并:通过
group by local-name()对每个Report_Entry的子节点按名称分组,若同一名称存在多个节点(如第四个Entry的两个EntryReason),则用string-join将值用分号拼接;否则直接取节点文本值。 - Map转换:每个分组转为键值对map,再通过
map:merge合并成单个对象map,最终组成数组元素。
该代码生成的输出是单个JSON数组节点,符合method="json"的要求,同时自动处理了重复字段的合并,完全匹配预期格式。
内容的提问来源于stack exchange,提问作者Mahari
相关产品推荐
相关产品推荐

