基于“名称”属性使用 XSLT 重命名 XML 节点

Posted

技术标签:

【中文标题】基于“名称”属性使用 XSLT 重命名 XML 节点【英文标题】:Renaming XML nodes with XSLT based on "name" attribute 【发布时间】:2021-05-20 22:05:02 【问题描述】:

我有这个 xml:

<?xml version="1.0" encoding="utf-8"?>
<Document Id="0c744468-67d8-4daa-8ff9-cbd23209c59d" Name="ROW_Easement (1)" TypeId="adde4dc1-0710-452a-82c7-9e5ac1bafe94" TypeName="ROW_Easement" OriginalName="106-19-47A.pdf" MimeType="application/pdf">
  <Field Name="File Name" Confidence="1.00" Page="1" Valid="True">106-19-47A</Field>
  <Section Name="tblPersonOfInterest">
    <SectionCollection Name="From" Count="4">
      <Section Name="From 1">
        <Field Name="Grantor" Confidence="1.00" Page="1" Valid="True" Location="1.713, 8.200, 6.487, 0.500">MARY E. GIBSON, and husband, E. J. GIBSON;
 ROSALIE L. SIEN, and husband, A. C. SIEN, Jr</Field>
      </Section>
     </SectionCollection>
   </Section>
</Document>

我想用“名称”属性值替换所有节点名称。到目前为止,我有:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
  <xsl:output method="xml" indent="yes" />
  <xsl:template match="@*|node()">
    <ROW_Easement>
      <xsl:for-each select="Field">
        <xsl:element name="translate(@Name, ' ', '_')">
          <xsl:value-of select="self::node()" />
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="Section">
        <xsl:element name="translate(@Name, ' ', '_')">
          <xsl:value-of select="self::node()" />
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="SectionCollection">
        <xsl:element name="translate(@Name, ' ', '_')">
          <xsl:value-of select="self::node()" />
        </xsl:element>
      </xsl:for-each>
    </ROW_Easement>
  </xsl:template>
</xsl:stylesheet>

结果是:

<?xml version="1.0" encoding="utf-8"?>
<ROW_Easement>
  <File_Name>106-19-47A</File_Name>
  <tblPersonOfInterest>
    
      
        MARY E. GIBSON, and husband, E. J. GIBSON;
 ROSALIE L. SIEN, and husband, A. C. SIEN, Jr
      
     
   </tblPersonOfInterest>
</ROW_Easement>

它在 SectionCollection 节点处中断,但我不明白为什么。

根据@michael.hor257k 的建议进行了更新 更新:使用我现在得到的第一个建议:

<?xml version="1.0" encoding="utf-8"?><ROW_Easement>0c744468-67d8-4daa-8ff9-cbd23209c59dROW_Easement (1)adde4dc1-0710-452a-82c7-9e5ac1bafe94ROW_Easement106-19-47A.pdfapplication/pdf
  <File_Name>1.001True106-19-47A</File_Name>
  <tblPersonOfInterest>
    <From>4
      <From_1>
        <Grantor>1.001True1.713, 8.200, 6.487, 0.500MARY E. GIBSON, and husband, E. J. GIBSON;
 ROSALIE L. SIEN, and husband, A. C. SIEN, Jr</Grantor>
      </From_1>
     </From>
   </tblPersonOfInterest>
</ROW_Easement>

【问题讨论】:

请发布minimal reproducible example,包括完整的 XML 输入示例和预期输出。 【参考方案1】:

没有说明您要保留哪些属性,但根据您的示例输入和您后来发布的代码,您似乎不想保留任何属性,但 Document 上的 Name 一个属性,所以

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

  <xsl:template match="Document">
    <ROW_Easement Name="@Name">
        <xsl:apply-templates/>
    </ROW_Easement>
  </xsl:template>
  
  <xsl:template match="*">
    <xsl:element name="translate(@Name, ' ', '_')">
        <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>
  
</xsl:stylesheet>

可能会更短,并避免列出您不想复制的所有属性。

【讨论】:

谢谢!我以为我对 Name 属性很清楚,但我会尽我所能在未来做得更多。【参考方案2】:

假设一个格式良好的 XML 输入,您可以使用这个简单的 XSLT-3.0 代码。它用@Name 属性替换所有元素的名称,并且只从输出中删除该属性。其余部分由xsl:mode复制:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
  <xsl:mode on-no-match="shallow-copy" />
  
  <xsl:template match="Document">
    <ROW_Easement>
        <xsl:apply-templates select="node()|@*" />
    </ROW_Easement>
  </xsl:template>
    
  <xsl:template match="*">
    <xsl:element name="translate(@Name, ' ', '_')">
        <xsl:apply-templates select="node()|@*[local-name()!='Name']" />
    </xsl:element>
  </xsl:template>
  
</xsl:stylesheet>

输出是:

<?xml version="1.0" encoding="UTF-8"?>
<ROW_Easement Id="0c744468-67d8-4daa-8ff9-cbd23209c59d"
              Name="ROW_Easement (1)"
              TypeId="adde4dc1-0710-452a-82c7-9e5ac1bafe94"
              TypeName="ROW_Easement"
              OriginalName="106-19-47A.pdf"
              MimeType="application/pdf">
   <File_Name Confidence="1.00" Page="1" Valid="True">106-19-47A</File_Name>
   <tblPersonOfInterest>
      <From Count="4">
         <From_1>
            <Grantor>
          </Grantor>
         </From_1>
      </From>
   </tblPersonOfInterest>
</ROW_Easement>

【讨论】:

几乎,这确实让我更接近了。更新帖子。【参考方案3】:

这可能不是最优雅的解决方案,但使用 @zx485 的回答我将 XSLT 更改为:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
  <xsl:mode on-no-match="shallow-copy" />
  <!-- Remove unnecessary attributes -->
  <xsl:template match="@Confidence" />
  <xsl:template match="@Id" />
  <xsl:template match="@TypeId" />
  <xsl:template match="@TypeName" />
  <xsl:template match="@OriginalName" />
  <xsl:template match="@MimeType" />
  <xsl:template match="@Page" />
  <xsl:template match="@Valid" />
  <xsl:template match="@Count" />
  <xsl:template match="@Location" />
  
  <!-- Rename nodes -->
  <xsl:template match="Document">
    <ROW_Easement>
        <xsl:apply-templates select="node()|@*" />
    </ROW_Easement>
  </xsl:template>
  
  <!-- Copy to new XML -->
  <xsl:template match="*">
    <xsl:element name="translate(@Name, ' ', '_')">
        <xsl:apply-templates select="node()|@*[local-name()!='Name']" />
    </xsl:element>
  </xsl:template>
  
</xsl:stylesheet>

它现在可以工作了!如果有人愿意提供改进方法的建议,仍然持开放态度。谢谢大家。

【讨论】:

以上是关于基于“名称”属性使用 XSLT 重命名 XML 节点的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 XSLT 从 XML 中删除名称空间

使用 XSLT 重命名节点

重命名元素的 XSLT 问题——更改命名空间

从 C# 代码重命名 XSLT 属性值

XML 元素名称中包含的 XSLT 命名空间 URI

使用命名空间时无法在 XSLT 中复制和修改属性