我正在寻找从INI到XML的某种转换,INI语法很简单。我不打算使用sed / awk / grep,这确实应该在XML工具中完成。
这可以通过常规XSL完成吗? 我听说过Xflat,但是我能用C编译的工具做到吗?例如xsltproc或xmlstarlet。
通用INI语法是这样的......
[section]
option = values
这将是像这样的xml ......
<section>
<option>values</option>
</section>
非常感谢任何帮助。
答案 0 :(得分:4)
可以使用常规XSL吗?
是的,XSLT 2.0为处理文本提供了比XSLT 1.0更多的功能。在XSLT中实现了非常复杂的文本处理,包括 a general LR(1) parser ,用于构建 specific grammars, such as JSON 和XPath的解析器。
特别要了解 unparsed-text()
,各种 string functions ,包括允许使用正则表达式 {{3 , (matches()
和 tokenize()
)以及 replace()
指令。
XSLT 1.0也有字符串函数(由XPath 1.0提供),但它没有正则表达式capabilty / functions,并且没有像XSLT 2.0函数unparsed-text()
那样的东西。最有用的XPath 1.0字符串函数包括: <xsl:analyze-string>
, substring()
, substring-before()
,< strong> substring-after()
, starts-with()
, string-length()
,尤其是 concat()
< / strong>功能。
可以使用DTD中的实体“读取”文件,正如Mads Hansen在他的回答中所解释的那样。另一种方法是在程序中读取启动转换的文件,然后将文件的内容作为字符串参数传递给转换。
更新: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, '
?
')[.]"/>
<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>
答案 1 :(得分: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>
答案 2 :(得分:1)
如果您可以使用XSLT 2.0处理器,则可以使用unparsed-text()
函数导入平面文件。
导入文件后,XPath 2.0中的传统字符串工具可以处理您的数据(正则表达式,翻译...),请参阅:http://www.w3.org/TR/xpath-functions/#string-functions。