1

我正在寻找从 INI 到 XML 的某种转换,INI 语法很简单。我不希望使用 sed/awk/grep,这确实应该在 XML 工具中完成。

这可以用常规 XSL 完成吗?我听说过 Xflat,但我可以通过用 C 编译的工具来做到这一点吗?例如 xsltproc 或 xmlstarlet。

通用 INI 语法是这样的......

[section]
option = values

这将是这样的xml...

<section>
<option>values</option>
</section>

任何帮助将不胜感激。

4

3 回答 3

4

这可以用常规 XSL 完成吗?

是的,而且 XSLT 2.0 提供了比 XSLT 1.0 更多的工具来处理文本。XSLT 中实现了非常复杂的文本处理,包括通用 LR(1) 解析器,用于构建特定语法的解析器,例如 JSON和 XPath。

特别是,了解unparsed-text()各种字符串函数,包括允许使用正则表达式matches()tokenize()replace())的函数以及<xsl:analyze-string>指令。

XSLT 1.0 也有字符串函数(由 XPath 1.0 提供),但是它缺少正则表达式能力/函数,并且没有像 XSLT 2.0 函数这样的东西unparsed-text()。最有用的 XPath 1.0 字符串函数包括:substring(), substring-before(), substring-after(), starts-with(), string-length(), concat(), 尤其是translate()函数。

正如 Mads Hansen 在他的回答中解释的那样,可以通过使用 DTD 中的实体来“读取”文件。另一种方法是在启动转换的程序中读取文件,然后将文件的内容作为字符串参数传递给转换。

更新:OP现在提供了具体数据,因此可以提供完整的解决方案:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vText" select=
 "unparsed-text('file:///c:/temp/delete/test.ini')"/>

 <xsl:variable name="vLines" as="xs:string*" select=
   "tokenize($vText, '&#xD;?&#xA;')[.]"/>

 <xsl:variable name="vLineCnt" select="count($vLines)"/>

 <xsl:variable name="vSectLinesInds" as="xs:integer*" select=
  "for $i in 1 to $vLineCnt
     return
       if(starts-with(normalize-space($vLines[$i]), '['))
         then $i
         else ()
  "/>

 <xsl:variable name="vSectCnt" select="count($vSectLinesInds)"/>

 <xsl:template match="/">
  <xsl:for-each select="$vSectLinesInds">
    <xsl:variable name="vPos" select="position()"/>
    <xsl:variable name="vInd" as="xs:integer" select="."/>

     <xsl:variable name="vthisLine" as="xs:string"
          select="$vLines[$vInd]"/>

    <xsl:variable name="vNextSectInd" select=
     "if($vPos eq $vSectCnt)
        then
          $vLineCnt +1
        else
          $vSectLinesInds[$vPos +1]
     "/>

   <xsl:variable name="vInnerLines" select=
   "$vLines
       [position() gt current()
      and
        position() lt $vNextSectInd
       ]

   "/>

   <xsl:variable name="vName" select=
    "tokenize($vthisLine, '\[|\]')[2]"/>

   <xsl:element name="{$vName}">
    <xsl:for-each select="$vInnerLines">
      <xsl:variable name="vInnerParts" select=
      "tokenize(., '[ ]*=[ ]*')"/>

      <xsl:element name="{$vInnerParts[1]}">
        <xsl:value-of select="$vInnerParts[2]"/>
      </xsl:element>
    </xsl:for-each>
  </xsl:element>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于任何 XML 文档(未使用)并且该文件 C:\temp\delete\test.ini 具有以下内容时:

[section1]
option1 = values1
option2 = values2
option3 = values3
option4 = values4
option5 = values5

[section2]
option1 = values1
option2 = values2
option3 = values3
option4 = values4
option5 = values5

[section3]
option1 = values1
option2 = values2
option3 = values3
option4 = values4
option5 = values5

产生了想要的正确结果

<section1>
   <option1>values1</option1>
   <option2>values2</option2>
   <option3>values3</option3>
   <option4>values4</option4>
   <option5>values5</option5>
</section1>
<section2>
   <option1>values1</option1>
   <option2>values2</option2>
   <option3>values3</option3>
   <option4>values4</option4>
   <option5>values5</option5>
</section2>
<section3>
   <option1>values1</option1>
   <option2>values2</option2>
   <option3>values3</option3>
   <option4>values4</option4>
   <option5>values5</option5>
</section3>
于 2011-12-08T13:18:57.447 回答
2

是的,您可以在 XSLT 中解析纯文本文件

如果您愿意,在 XSLT 2.0 中这样做可能会更容易。

在 XSLT 2.0 中,您可以使用unparsed-text()函数读取文件,使用tokenize()将其拆分为行。

<xsl:for-each select="tokenize(unparsed-text($in), '\r?\n')">
 ...
</xsl:for-each>

在 XSLT 1.0中:您可以通过使用外部实体引用文本文件将其合并到 XML 文件中来读取许多平面文本文件(只要它们不包含任何会导致 XML 解析错误的字符/模式)。文件中的文本在解析时将包含在 XML 文件中。

<!DOCTYPE foo [
<!ENTITY bar SYSTEM "bar.txt">
]>
<foo>
&bar;
</foo>
于 2011-12-08T13:13:22.370 回答
1

如果您可以使用 XSLT 2.0 处理器,那么您就拥有了unparsed-text()可以导入平面文件的功能。

导入文件后,您可以使用 XPath 2.0 中的传统字符串工具来处理数据(正则表达式、翻译...),请参阅:http ://www.w3.org/TR/xpath-functions/#string-functions 。

于 2011-12-08T10:54:38.383 回答