PAYATHON 2026

如何使用 XSLT 1.0 提取 EDI 204 中 LAD10 的不重复值?

数据小许

结论

可以使用 XSLT 1.0 的 Muenchian 分组:通过 xsl:key 按 LAD10 的值建立索引,再从每组的第一个 LAD 节点输出 LAD10。

前提是 EDI 数据已经转换成 XML。题目提供的是以 * 分隔的原始 EDI 文本,标准 XSLT 1.0 不能直接将它作为 XML 源文档处理。由于尚未提供实际 XML,下面的元素名称和路径均为示例,需要根据转换后的结构调整。

先确认 LAD10 的位置和 XML 结构

以题目中的第一条记录为例:

LAD*PCS*0*K*1.308***PQ*000000000000177808*ON*200002415***APPLETON
ESTATE V/X SIGNATURE BLEND

以 * 为分隔符,LAD 是段标识,其后的元素从 LAD01 开始编号。连续分隔符之间的空元素也要计入位置,因此这里的 LAD09 是 ON,LAD10 是 200002415。

后续多条记录的 LAD10 也是 200002415,该值只需输出一次。商品名称和其他字段是否相同,不影响这次去重。

假设 EDI 解析后,每个 LAD 段对应一个 LAD 元素,第十个字段对应其子元素 LAD10。下面从原始数据中取几个值构造简化示例,只保留去重需要的字段:

<EDI204>
  <LAD><LAD10>200002415</LAD10></LAD>
  <LAD><LAD10>200002415</LAD10></LAD>
  <LAD><LAD10>200002398</LAD10></LAD>
  <LAD><LAD10>200002398</LAD10></LAD>
  <LAD><LAD10>200002357</LAD10></LAD>
</EDI204>

使用 XSLT 1.0 输出不重复值

下面的完整样式表适用于上述无命名空间的 XML 结构。它在整个源文档中去重,跳过缺失或仅含空白的 LAD10,每行输出一个值:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text" encoding="UTF-8"/>

  <xsl:key
    name="lad-by-lad10"
    match="LAD[normalize-space(LAD10) != '']"
    use="normalize-space(LAD10)"/>

  <xsl:template match="/">
    <xsl:for-each select="//LAD[
      normalize-space(LAD10) != ''
      and generate-id() = generate-id(
        key('lad-by-lad10', normalize-space(LAD10))[1]
      )
    ]">
      <xsl:value-of select="normalize-space(LAD10)"/>
      <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

对于上面的简化 XML,输出为:

200002415
200002398
200002357

这是简化示例的输出,不代表题目中全部 LAD 记录的完整去重结果。

为什么每个值只会输出一次

xsl:key 根据 LAD10 的值索引 LAD 节点,把值相同的节点归为一组。例如:

key('lad-by-lad10', '200002415')

这个表达式会返回当前源文档中按该键匹配到的所有 LAD10 为 200002415 的 LAD 节点。

再用 [1] 选出该组在文档顺序中的第一个节点,并比较节点身份:

generate-id() = generate-id(
  key('lad-by-lad10', normalize-space(LAD10))[1]
)

只有组内第一个节点满足条件,所以同一个值只输出一次。未添加 xsl:sort 时,结果按各值在源文档中首次出现的顺序排列。

XSLT 1.0 使用的 XPath 1.0 没有标准的 distinct-values() 函数,因此这里采用键索引分组。

如何适配实际文件

XML 元素名称和路径必须对应

EDI 解析器不一定生成 <LAD><LAD10>...</LAD10></LAD> 这种结构。实际字段可能使用其他名称,也可能通过属性保存字段编号。需要同时调整:

  • xsl:key 的 match:匹配实际的 LAD 段节点。
  • xsl:key 的 use:读取实际的第十个字段。
  • xsl:for-each 的选择路径、过滤条件以及输出表达式。

仅修改其中一处,可能导致索引和查询使用不同的节点或值。

有命名空间时使用前缀

如果源 XML 使用默认命名空间,在 XPath 1.0 中直接写 LAD 和 LAD10 无法匹配这些元素。需要在样式表中声明一个前缀,并将它绑定到源 XML 的实际命名空间 URI。

然后将相关名称统一改为带前缀的形式,例如:

<xsl:key
  name="lad-by-lad10"
  match="e:LAD[normalize-space(e:LAD10) != '']"
  use="normalize-space(e:LAD10)"/>

这里的 e 必须先在样式表上声明,选择和输出表达式中的 LAD、LAD10 也要同步改为 e:LAD、e:LAD10。

明确空白处理和去重范围

示例使用 normalize-space(),会去掉首尾空白,并将内部连续的 XML 空白字符折叠成一个空格。如果需要逐字符区分值,应统一修改键值、查询值和输出值,避免部分值经过规范化,部分保留原样。

LAD10 应按字符串处理。将它转成数字再比较,可能丢失前导零。

该样式表按整个 XML 源文档去重。如果一个源文档包含多个 EDI 204 交易,且要求每个交易分别去重,需要将交易范围加入分组键并限制选择范围。仅缩小 for-each 的路径,不能改变键的文档级作用范围。

如果目前只有原始 EDI 文件,应先按文件声明的分隔符解析成 XML。题目示例中的商品名称存在换行,不能仅凭展示出来的换行判断实际段边界。

备注:内容仅供参考。