XSLT 映射。在 for each 循环中动态设置 xml 节点名称

XSLT mapping. Dynamically setting xml node names in a for each loop

您好,我的 xml 中有重复的节点名称,我想让它们中的每一个都是唯一的。 由于节点数量未知,我想为每个循环实现一个,并使用 XSLT 为每个 UserGroup 节点的名称添加一个计数器。

我正在使用:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

我的输入xml是:

<groups>
        <UserGroup>
                <integrationKey>xxa</integrationKey>
                <uid>001</uid>]
        </UserGroup>
        <UserGroup>
                <integrationKey>xxb</integrationKey>
                <uid>002</uid>
        </UserGroup>
</groups>

如何使用 XSLT 将 xml 转换为:

<groups>
        <UserGroup1>
                <integrationKey>xxa</integrationKey>
                <uid>001</uid>]
        </UserGroup1>
        <UserGroup2>
                <integrationKey>xxb</integrationKey>
                <uid>002</uid>
        </UserGroup2>
</groups>

编辑 嗨,原因是我正在处理构建的 xml 消息负载,然后需要将其转换为 JSON(组部分附加到主 xml)。有效载荷将要接受的目标系统接受某种不包括用户组的“格式”。不幸的是,用户组的数量是动态的,映射的控制有限。 我试图在 Groovy 中构建 Json 并使用:

import groovy.json.JsonSlurper
import groovy.json.JsonOutput
import groovy.json.JsonBuilder

    def body = """

{"B2BCustomer":{"integrationKey":"","customerID":"xxx","email":"xxx","name":"xxx","uid":"xxx","businessUnit":{"BU":{"integrationKey":"","code":"xxx"}},"groups":[{"UserGroup":{"integrationKey":"xxx","uid":"xxx"},"UserGroup":{"integrationKey":"yyy","uid":"yyy"}}]}}"""
 
    //Setup output JSON
    def jsonParser = new JsonSlurper();
    def jsonObject = jsonParser.parseText(body);
    body = JsonOutput.toJson(jsonObject["B2BCustomer"]);
    output = jsonParser.parseText(body);
    jsonString = jsonParser.parseText(body);
    
    //Create default b2b unit JSON
    if(output.containsKey("defaultB2BUnit")){
        output.remove("defaultB2BUnit"); 
        defaultB2BUnit = JsonOutput.toJson(jsonString["defaultB2BUnit"]);
        jsonObject = jsonParser.parseText(defaultB2BUnit);
        defaultB2BUnit =  JsonOutput.toJson(jsonObject["B2BUnit"]);
        output.put("defaultB2BUnit", defaultB2BUnit);
    }
    //Create businessUnit JSON
    if(output.containsKey("businessUnit")){
        output.remove("businessUnit"); 
        businessUnit = JsonOutput.toJson(jsonString["businessUnit"]);
        jsonObject = jsonParser.parseText(businessUnit);
        businessUnit =  JsonOutput.toJson(jsonObject["BU"]);
        output.put("businessUnit", businessUnit);
    }
    //Create groups JSON
    if(output.containsKey("groups")){
        output.remove("groups"); 
        groups = JsonOutput.toJson(jsonString["groups"]);
        jsonObject = jsonParser.parseText(groups);
        groups =  JsonOutput.toJson(jsonObject["UserGroup"]);
        output.put("groups", groups);
    }
    //Build output JSON
    def builder = new JsonBuilder();
    builder(output);
    def builderString = builder.toString().replace('"{\', '{').replace('\','').replace('}"','}').replace('"[','[').replace(']"',']');
    println builderString;

如果键不重复,这会起作用(它只会拉出一个用户组。我的计划是运行这个代码仍然存在,但是组在循环中参与以获得所需的JSON输出。

如果有人有办法让我不必进行字符串替换,那也很棒。很遗憾,我无法更改目标系统元数据的结构。

当前输出:

{
    "integrationKey": "",
    "customerID": "xxx",
    "email": "xxx",
    "name": "xxx",
    "uid": "xxx",
    "businessUnit": {
        "integrationKey": "",
        "code": "xxx"
    },
    "groups": [{
            "integrationKey": "yyy",
            "uid": "yyy"
        }
    ]
}

期望的输出:

{
    "integrationKey": "",
    "customerID": "xxx",
    "email": "xxx",
    "name": "xxx",
    "uid": "xxx",
    "businessUnit": {
        "integrationKey": "",
        "code": "xxx"
    },
    "groups": [{
            "integrationKey": "xxx",
            "uid": "xxx"
        },
        {
            "integrationKey": "yyy",
            "uid": "yyy"
        }
    ]
}

额外说明 我正在考虑将组存储在交换 属性 和 运行 自定义 xml -> Json Groovy 脚本来完全按照我想要的方式构建它,而不会受到 JSON slurper 等的奇怪影响。 提前感谢您的努力和建议!

鉴于 XSLT 3,一种方法是使用累加器:

  <xsl:accumulator name="UserGroupCounter" as="xs:integer" initial-value="0">
    <xsl:accumulator-rule match="groups" select="0"/>
    <xsl:accumulator-rule match="groups/UserGroup" select="$value + 1"/>
  </xsl:accumulator>
  
  <xsl:template match="UserGroup">
    <xsl:element name="{name()}{accumulator-before('UserGroupCounter')}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>

  <xsl:mode on-no-match="shallow-copy" use-accumulators="UserGroupCounter"/>

我同意关于结果格式不是好的 XML 格式的评论。如果您需要一些元素索引,通常的建议是不要将其放入元素名称中,而是使用属性,例如

  <xsl:accumulator name="UserGroupCounter" as="xs:integer" initial-value="0">
    <xsl:accumulator-rule match="groups" select="0"/>
    <xsl:accumulator-rule match="groups/UserGroup" select="$value + 1"/>
  </xsl:accumulator>
  
  <xsl:template match="UserGroup">
    <xsl:copy>
      <xsl:attribute name="userGroupIndex" select="accumulator-before('UserGroupCounter')"/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:mode on-no-match="shallow-copy" use-accumulators="UserGroupCounter"/>