【问题标题】:Regular text file to XML using XSLT使用 XSLT 将常规文本文件转换为 XML
【发布时间】:2011-04-15 11:18:14
【问题描述】:

我有一个看起来像这样的文本文件:

XXX^YYYY^AAAAA^XXXXXX^AAAAAA....

字段使用插入符号(^)分隔,我的假设是:

第一个字段 = NAME
第二个字段 = 姓
第三个字段 = 地址

等等。

我想使用 xsl (XSLT) 将其转换为有效的 XML。 如:

<name>XXX</name>
<l_name>YYYY</l_name>

我知道使用 Perl 可以轻松完成,但如果可能的话,我需要使用 XSLT。

【问题讨论】:

  • 好问题,+1。有关完整的 XSLT 1.0 解决方案以及对 XSLT 2.0 更强大的文本处理功能的描述以及指向真实世界 XSLT 2.0 文本处理示例的指针,请参阅我的答案。

标签: xml xslt


【解决方案1】:

可以使用标准 XSLT 2.0 函数 unparsed-text() 读取文本(非 XML)文件。

然后可以使用标准 XPath 2.0 函数 tokenize() 和另外两个 standard XPath 2.0 functions that accept regular a expression 作为它们的参数之一 -- matches()replace()

XSLT 2.0 有自己强大的 instructions to handle text processing using regular expressions:&lt;xsl:analyze-string&gt;&lt;xsl:matching-substring&gt;&lt;xsl:non-matching-substring&gt; 指令。

在这个真实的示例中使用这些函数和指令查看 XSLT 文本处理的一些更强大的功能:XSLT solution to the WideFinder problem

最后,这是一个 XSLT 1.0 解决方案

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 xmlns:my="my:my" exclude-result-prefixes="ext my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <my:fieldNames>
  <name>FirstName</name>
  <name>LastName</name>
  <name>City</name>
  <name>State</name>
  <name>Zip</name>
 </my:fieldNames>

 <xsl:variable name="vfieldNames" select=
  "document('')/*/my:fieldNames"/>

 <xsl:template match="/">
  <xsl:variable name="vrtfTokens">
   <xsl:apply-templates/>
  </xsl:variable>

  <xsl:variable name="vTokens" select=
       "ext:node-set($vrtfTokens)"/>

  <results>
   <xsl:apply-templates select="$vTokens/*"/>
  </results>
 </xsl:template>

 <xsl:template match="text()" name="tokenize">
  <xsl:param name="pText" select="."/>

     <xsl:if test="string-length($pText)">
       <xsl:variable name="vWord" select=
       "substring-before(concat($pText, '^'),'^')"/>

       <word>
        <xsl:value-of select="$vWord"/>
       </word>

       <xsl:call-template name="tokenize">
        <xsl:with-param name="pText" select=
         "substring-after($pText,'^')"/>
       </xsl:call-template>
     </xsl:if>
 </xsl:template>

 <xsl:template match="word">
  <xsl:variable name="vPos" select="position()"/>

  <field>
      <xsl:element name="{$vfieldNames/*[position()=$vPos]}">
      </xsl:element>
      <value><xsl:value-of select="."/></value>
  </field>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于以下 XML 文档时:

<t>John^Smith^Bellevue^WA^98004</t>

产生想要的正确结果

<results>
   <field>
      <FirstName/>
      <value>John</value>
   </field>
   <field>
      <LastName/>
      <value>Smith</value>
   </field>
   <field>
      <City/>
      <value>Bellevue</value>
   </field>
   <field>
      <State/>
      <value>WA</value>
   </field>
   <field>
      <Zip/>
      <value>98004</value>
   </field>
</results>

【讨论】:

  • +1 这个“我有一个文本文件”需要 XSLT 2.0。 (除非您有 DTD 的内部子集感知 XML 解析器)
  • @Alejandro:实体是 XML 文档的一部分——OP 希望能够读取给定其 URL 的任何文件——可能文件 URL 将作为参数传递给样式表。顺便说一句,我用完整的 XSLT 1.0 解决方案附加了我的答案:)
  • @Dimitre:此 XML 包装器 &lt;!DOCTYPE test [&lt;!ENTITY text SYSTEM "test.txt"&gt;]&gt;&lt;test&gt;&amp;text;&lt;/test&gt;test.txt 文件为 John^Smith^Bellevue^WA^98004,产生相同的输出。
  • @Alejandro:是的。然而,这与 XSLT 无关——仅与 XML 有关。另外,我们不要忘记,出于安全考虑,许多 XML 解析器默认禁用实体。
  • @Dimitre:是的。我认为这是一件坏事:访问外部资源的安全问题应该由系统处理。完整的 DTD 支持有很多用途......比如使用 &lt;!ENTITY uri SYSTEM "#" NDATA uri&gt;unparsed-entity-uri('uri') 获取文档 URI
【解决方案2】:

Tokenizing and sorting with XSLT 1.0

如果您使用 xslt 2.0,它会简单得多: fn:tokenize(字符串,模式)

Example: tokenize("XPath is fun", "\s+")
Result: ("XPath", "is", "fun")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-11
    • 1970-01-01
    • 1970-01-01
    • 2012-02-06
    • 2014-11-15
    • 1970-01-01
    相关资源
    最近更新 更多