将INI转换为XML?或者任何通用的遗留平面文件?XSL吗?从xmlstarlet或xsltproc



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

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

通用INI语法如下:

[section]
option = values

在XML中应该是这样的…

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

这可以用常规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() 函数。

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

Update: 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:tempdeletetest.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>

是的,您可以用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>

如果可以使用XSLT 2.0处理器,则可以使用可以导入平面文件的unparsed-text()函数。

导入文件后,就可以使用XPath 2.0中的传统字符串工具来处理数据(regex、translate…),参见:http://www.w3.org/TR/xpath-functions/#string-functions。

最新更新