-
30-09-2019 - |
質問
CLR4.0のXSLCompiledTransformを使用してXSLファイルで並べ替えようとするのに問題があります。これが私のサンプルXMLファイルです(注:2番目の後のスペースがあります <foo>
エレメント):
<?xml version="1.0" encoding="utf-8"?>
<reflection>
<apis>
<api id="A">
<foos>
<foo/>
</foos>
</api>
<api id="B">
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
次のXSLファイルを適用するとき:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.1">
<xsl:template match="/">
<html>
<body>
<table>
<xsl:apply-templates select="/reflection/apis/api">
<xsl:sort select="@id" />
</xsl:apply-templates>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="api">
<tr>
<td>
<xsl:value-of select="@id" />
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
次の結果が得られます。
<html>
<body>
<table>
<tr>
<td>B</td>
</tr>
<tr>
<td>A</td>
</tr>
</table>
</body>
</html>
ただし、2番目のスペースの後にスペースを削除した場合 <foo>
要素、結果のファイルは正しくソートされます。これは、おそらくXSLCompiledTransformのバグのように思えますが、誰かが回避策を持っていることを望んでいました。
編集:誰かがそれを再現するのに苦労している場合、私が使用しているコードは次のとおりです。
XslCompiledTransform xslt = new XslCompiledTransform();
XsltSettings transformSettings = new XsltSettings(true, true);
xslt.Load("CreateVSToc.xsl", transformSettings, new XmlUrlResolver());
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreWhitespace = true;
Stream readStream = File.Open("reflection.xml", FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
using (XmlReader reader = XmlReader.Create(readStream, readerSettings))
{
Stream outputStream = File.Open("toc.xml", FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete);
using (XmlWriter writer = XmlWriter.Create(outputStream, xslt.OutputSettings))
{
XsltArgumentList arguments = new XsltArgumentList();
xslt.Transform(reader, arguments, writer);
}
}
解決 4
@Russ Ferri、答えてくれてありがとう。それは私を正しい方向に向けました。 xslcompiledtransformのバグは、要素の属性によってソートする場合、実際にその要素の最初の子要素の値によってソートされると思われます。ラスが指摘したように、これは私の元の変換ファイルで正しく並べ替えます:
<reflection>
<apis>
<api id="C">
<id>C</id>
<foos>
<foo/>
</foos>
</api>
<api id="B">
<id>B</id>
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
しかし、これもそうです:
<reflection>
<apis>
<api id="C">
<anyElementName>C</anyElementName>
<foos>
<foo/>
</foos>
</api>
<api id="B">
<anyElementName>B</anyElementName>
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
実際、属性名は完全に無視されているため、このようなもので変換を実行すると:
<reflection>
<apis>
<api id="C">
<anyElementName>Z</anyElementName>
<foos>
<foo/>
</foos>
</api>
<api id="A">
<anyElementName>Y</anyElementName>
<foos>
<foo/>
</foos>
</api>
<api id="B">
<anyElementName>X</anyElementName>
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
結果は次のようになります:
<html>
<body>
<table>
<tr>
<td>B</td>
</tr>
<tr>
<td>A</td>
</tr>
<tr>
<td>C</td>
</tr>
</table>
</body>
</html>
あなたがで並べ替えていた場合、これは正しいソートです <anyElementName>
要素
他のヒント
疑わしいことに、それぞれのXMLの場合 api
要素は次のように変更され、結果は予想どおりにソートされます。
<api id="apiId">
<id>apiId</id>
<foos>
<foo />
</foo>
</api>
さらに、a)それぞれのXMLの場合 api
要素は、を削除するように変更されています id
完全に属性
<api>
<id>apiId</id>
<foos>
<foo />
</foo>
</api>
およびb)のみ 2番目 への参照 @id
XSLファイルはに変更されます id
, 、結果はまだアルファベット順にソートされます!
それは可能です XslCompiledTransform
名前付きの子要素を並べ替えようとしています id
名前の属性の代わりに id
, 、またはこれはただ馬鹿げた運になる可能性があります。いずれにせよ、私はそれが名前付きの子要素を正しく並べ替えてくれることを確認しました id
.
これを念頭に置いて、2つの回避策を考えることができますが、どちらも変換プロセスをある程度制御する必要があります。
アプローチ1:XMLを変更できます
元のXMLを書き込むプロセスを変更して指定します id
anが含む最初の要素として api
エレメント。次に、XSLを更新して、参照を置き換えます @id
と id
.
アプローチ2:XSLを適用する前にXMLを前処理することができます
XSL変換を使用して、 id
の子要素への属性 api
, 、次に、あなたと同じXSLを適用します アプローチ1 中間XMLドキュメントに。大規模なXMLファイルを処理すると、ドキュメントを2回変換することは明らかに望ましくありません。
次のXSLでは、元のXMLから中間XMLに届きます。
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.1">
<!-- recursively copy each element (including root) -->
<xsl:template match="*|/">
<xsl:copy>
<!-- xsl:copy ignores attributes, copy those as well -->
<xsl:copy-of select="@*"/>
<!-- continue to deep copy the element -->
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<!-- for api elements, move the id attribute into an element -->
<xsl:template match="api">
<api>
<id>
<xsl:value-of select="@id"/>
</id>
<!-- continue deep copy of api element contents -->
<xsl:apply-templates />
</api>
</xsl:template>
</xsl:stylesheet>
これが役立つことを願っています!
Sytyleシートバージョンを何でも「1.0」に変更すると機能します> = 2.0
私は問題を再現しようとしていませんが、あなたのXSLに明らかに間違っていることは見当たりません。 XSLエンジンの問題かバグかどうかを確認するために私が探求するものは次のとおりです。 <foo>
ID「A」を「C」に変更します。 「b」、「c」という順序で出力を取得しますか?つまり、実際にIDで並べ替えていることを確認してください。