【问题标题】:Passing Map to XSLT and changing values by keys将 Map 传递给 XSLT 并通过键更改值
【发布时间】:2020-07-20 16:07:53
【问题描述】:

我有一个Map,其中包含几个我想使用 XSLT 应用于 XML 文档的条目。

每当它在 XML 中找到 TESTWORD 时,获取以下键并将其替换为映射中的适当值。同时替换该节点的名称。

输入:

<Company>
   <Employee>
      <FirstName>Homer</FirstName>
      <LastName>Simpson</LastName>
      <ContactNo>1234567890</ContactNo>
      <Address>
         <City>Springfield</City>
         <Note>TESTWORD key1</Note> <!-- change this -->
      </Address>
   </Employee>
   <Employee>
      <FirstName>Peter</FirstName>
      <LastName>Griffin</LastName>
      <ContactNo>0987654321</ContactNo>
      <Address>
         <City>Quahog</City>
         <Note>TESTWORD key2</Note> <!-- change this -->
      </Address>
   </Employee>
</Company>

预期输出:

<Company>
   <Employee>
      <FirstName>Homer</FirstName>
      <LastName>Simpson</LastName>
      <ContactNo>1234567890</ContactNo>
      <Address>
         <City>Springfield</City>
         <NewElem>my value</NewElem> <!-- changed -->
      </Address>
   </Employee>
   <Employee>
      <FirstName>Peter</FirstName>
      <LastName>Griffin</LastName>
      <ContactNo>0987654321</ContactNo>
      <Address>
         <City>Quahog</City>
         <NewElem>another value</NewElem> <!-- changed -->
      </Address>
   </Employee>
</Company>

Java:

Map<String, String> map = new HashMap<>();
map.put("key1", "my value");
map.put("key2", "another value");

try {
    TransformerFactory factory = TransformerFactory.newInstance();
    Source xslt = new StreamSource(new File("doc.xslt"));
    Transformer transformer = factory.newTransformer(xslt);
    transformer.setParameter("map", map); // Passing map as parameter

    Source text = new StreamSource(new File("doc.xml"));
    transformer.transform(text, new StreamResult(new File("newXml.xml")));
} catch (URISyntaxException | TransformerException ex) {}

XSLT:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output indent="yes" method="xml" encoding="utf-8" />

    <xsl:key name="map" /> <!-- the map with my keys and values -->

    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Note[.='TESTWORD key1']"> <!-- iterate all params -->
        <xsl:element name="NewElem">
            <xsl:value-of select="$key1" /> <!-- replace all values -->
        </xsl:element>
    </xsl:template>

    <!--
    <xsl:template match="Note[.='TESTWORD key2']">
        <xsl:element name="NewElem">
            <xsl:value-of select="$key2" />
        </xsl:element>
    </xsl:template> 
    -->

</xsl:stylesheet>

你可以在这里查看:http://xsltransform.net/3N7GxDd

【问题讨论】:

  • 声明&lt;xsl:key name="map" /&gt; &lt;!-- the map with my keys and values --&gt; 的部分没有任何意义。密钥不是将映射从宿主语言传递到 XSLT 的工具。传入映射的唯一方法是在 XSLT/XPath 中具有等效结构,即在 XSLT 3 和 Saxon 9.8 或更高版本中。但是您将使用xsl:param,如stackoverflow.com/a/62996659/252228 所示,您需要将Java 映射转换为XdmMap。

标签: java xml xslt


【解决方案1】:

这是使用 XSLT 1.0 并在 XSLT 中使用 Xalan 的一种方法:

我创建了一个包含静态地图的简单类:

package samples;

import java.util.Map;

public class StaticMap {
    private static Map<String, String> map;
    
    public static Map<String, String> getMap() {
        return map;
    }
    
    public static void setMap(Map<String, String> map) {
        StaticMap.map = map;
    }
     
    public static String getValue(String key) {
        return map.get(key);
    }
}

我没有将地图传递给 XSLT,而是将地图加载到我的静态地图类中:

            Map<String, String> map = new HashMap<>();
            map.put("key1", "my value");
            map.put("key2", "another value");

            try {
                TransformerFactory factory = TransformerFactory.newInstance();
                Source xslt = new StreamSource(new File("C:\\Users\\Public\\Documents\\doc.xslt"));
                Transformer transformer = factory.newTransformer(xslt);
                StaticMap.setMap(map);

                Source text = new StreamSource(new File("C:\\Users\\Public\\Documents\\doc.xml"));
                transformer.transform(text, new StreamResult(new File("C:\\Users\\Public\\Documents\\newXml.xml")));
            } catch (Exception ex) {}

这是我的 XSLT。请注意,在您的 XSLT 中,Node 模板与文本值为“TESTWORD key1”的元素匹配,我将其更改为仅查找以“TESTWORD”开头的值:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:staticMap="xalan://samples.StaticMap"
    version="2.0">
    <xsl:output indent="yes" method="xml" encoding="utf-8" />


    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Note[substring(., 1, 8) = 'TESTWORD']"> <!-- iterate all params -->
        <xsl:element name="NewElem">
            <xsl:value-of select="staticMap:getValue(substring(.,10))" /> <!-- replace all values -->
        </xsl:element>
    </xsl:template>
    
</xsl:stylesheet>

这是我的输出:

<?xml version="1.0" encoding="utf-8"?><Company>
       
    <Employee>
              
        <FirstName>Homer</FirstName>
              
        <LastName>Simpson</LastName>
              
        <ContactNo>1234567890</ContactNo>
              
        <Address>
                     
            <City>Springfield</City>
                     
            <NewElem>my value</NewElem>
             
            <!-- change this -->
                  
        </Address>
           
    </Employee>
       
    <Employee>
              
        <FirstName>Peter</FirstName>
              
        <LastName>Griffin</LastName>
              
        <ContactNo>0987654321</ContactNo>
              
        <Address>
                     
            <City>Quahog</City>
                     
            <NewElem>another value</NewElem>
             
            <!-- change this -->
                  
        </Address>
           
    </Employee>
    
</Company>

【讨论】:

  • 请注意,您在回答中使用 Xalan:xml.apache.org/xalan-j
  • @user1170330,我已经更新了我的答案,包括使用了 Xalan。谢谢。
【解决方案2】:

我的版本 (XSLT 2.0) 在模板内设置 xsl:param

    <xsl:param name="key1"/>
    <xsl:param name="key2"/>
    <xsl:param name="key3"/>
    <xsl:param name="key4"/>
    <xsl:template match="Note[contains(.,'TESTWORD key1')]">
         <xsl:element name='{$elemName}'>
    <xsl:value-of select="$key1" />
        </xsl:element>
    </xsl:template>
    <xsl:template match="Note[contains(.,'TESTWORD key2')]">
         <xsl:element name='{$elemName}'>
    <xsl:value-of select="$key2" />
        </xsl:element>
    </xsl:template>
    <xsl:template match="Note[contains(.,'TESTWORD key3')]">
         <xsl:element name='{$elemName}'>
    <xsl:value-of select="$key3" />
        </xsl:element>
    </xsl:template>
    <xsl:template match="Note[contains(.,'TESTWORD key4')]">
         <xsl:element name='{$elemName}'>
    <xsl:value-of select="$key4" />
        </xsl:element>
    </xsl:template>

当调用 Java setParameter 时,我传递值 | XSL 样式表的外部参数。

        // create Transformer
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(xsl);
        // set parameters
        transformer.setParameter("key1", "replaced value1");
        transformer.setParameter("key2", "replaced value2");
        transformer.setParameter("key3", "replaced value3"); 
        transformer.setParameter("key4", "replaced value4"); 

Martin Honnen XSLT 3.0 版本:

    <xsl:param name="map" as="map(xs:string, xs:string)"
        select="
            map {
            'key1': 'replaced value1',
            'key2': 'replaced value2',
            'key3': 'replaced value3',
            'key4': 'replaced value4'
            }"/>

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

    <xsl:variable name="map-keys" select="map:keys($map)"/>

    <xsl:template
        match="
            Note[some $key in $map-keys
                satisfies . = 'TESTWORD ' || $key]">
        <xsl:element name='{$elemName}'>
            <xsl:value-of select="$map($map-keys[current() = 'TESTWORD ' || .])"/>
        </xsl:element>
    </xsl:template>

日志记录:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/ml/fiona/.m2/repository/org/slf4j/slf4j-log4j12/1.7.16/slf4j-log4j12-1.7.16.jar!/org/slf4j/impl/StaticLoggerBinder.class]

writing /xslt/employee.xml to database allegro... on 2020-07-23T15:27:50.805440900

Total One document(s) in One batch is completed on 2020-07-23T15:27:51.115639400 

User audited as 'super' has modified /xslt/employee.xml

<?xml version="1.0" encoding="UTF-8"?>
<Company>
   <Employee>
      <FirstName>John</FirstName>
      <LastName>Smith</LastName>
      <ContactNo>007007</ContactNo>
      <Address>
         <City>Lincolnshire</City>
         <nameOnChanged>replaced value1</nameOnChanged>
      </Address>
   </Employee>
   <Employee>
      <FirstName>John</FirstName>
      <LastName>Doe</LastName>
      <ContactNo>007009</ContactNo>
      <Address>
         <City>Putney</City>
         <nameOnChanged>replaced value2</nameOnChanged>
      </Address>
   </Employee>
   <Employee>
      <FirstName>Jane</FirstName>
      <LastName>Smith</LastName>
      <ContactNo>007000</ContactNo>
      <Address>
         <City>Oxfordshire</City>
         <nameOnChanged>replaced value3</nameOnChanged>
      </Address>
   </Employee>
   <Employee>
      <FirstName>Jane</FirstName>
      <LastName>Doe</LastName>
      <ContactNo>007001</ContactNo>
      <Address>
         <City>Glasgow</City>
         <nameOnChanged>replaced value4</nameOnChanged>
      </Address>
   </Employee>
</Company>

【讨论】:

  • 究竟有什么好处?我仍然需要分别指定每个匹配项:&lt;xsl:template match="Note[.='TESTWORD key1']"&gt;...&lt;/xsl:template&gt;。另外,当使用xsl:variable 而不是xsl:param 时,我无法从Java 传递参数。
  • 请查看修订版。 (我重构了 xml 内容……对不起,我有边思考边涂鸦的习惯……) xsl:variablexsl:param 在语法上相似。但是该技术相当不同……@Martin Honnen(no offense intended):尽管map:keys($map) 在我的 Oxygen 中有效,但在 Java API 中却失败了。看起来像 XSLT 3 的可组合性问题。所以XdmMap 的功效还有待观察……
  • @FionaChen,我建议将 XdmMap 用于带有 Saxon 9.8 及更高版本的 XSLT 3,并使用其 s9api。 Java 平台本身只有一个 XSLT 1 处理器,因此它肯定不允许使用 XPath 3.1 映射,而与您使用的 API 无关。
猜你喜欢
  • 2015-08-02
  • 2021-03-29
  • 2021-09-11
  • 2010-12-04
  • 1970-01-01
  • 2015-08-16
  • 1970-01-01
  • 1970-01-01
  • 2013-09-27
相关资源
最近更新 更多