我对游戏完全不熟悉。我正在进行的项目需要能够交替渲染为HTML或文本。我决定尝试使用XSLT,这样我就可以在不改变底层代码结构的情况下修改输出。 HTML输出很好。尝试将转换写入文本时有点迷失。原因如下:
我正在转换的xml属于这种类型的结构:
<Data>
<Text x="0" y="1">First Line</Text>
<Text x="12" y="1">Continued on Same Line</Text>
<Text x="36" y="1">Still Going</Text>
<Text x="5" y="2">Slightly Indented New Line</Text>
</Data>
我用于html的基本模板工作正常。我现在正在尝试为文本输出创建一个模板,即
<xsl:output method="text"/>
但我还是无法设计一种基于“x”和“y”值(或坐标)从Text元素构建字符串的方法,这就是我需要对文本输出做的事情,这样才能写入上面的示例xml中的文本文件是:
First Line Continued on Same Line Still Going Slightly Indented New Line
所以,如果我在代码中做相同的操作,它可能看起来像:
private string SomeMethod(XPathNavigator TestNav)
{
int iRow = 0;
int iColumn = 0;
XPathNodeIterator lineIterator = TestNav.SelectChildren("Data", "");
StringBuilder text = new StringBuilder();
while (lineIterator.MoveNext())
{
XPathNavigator curNav= lineIterator.Current;
XPathExpression Exp = curNav.Compile("*[@x]|*/*[@x]");
Exp.AddSort("@y", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number);
Exp.AddSort("@x", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number);
XPathNodeIterator positionIterator = curNav.Select(Exp);
while (positionIterator.MoveNext())
{
String elValue = positionIterator.Current.InnerXml;
int xTxt = int.Parse(positionIterator.Current.GetAttribute("x", ""));
int yTxt = int.Parse(positionIterator.Current.GetAttribute("y", ""));
if (iRow < yTxt)
{
string newLines = new string('\n', yTxt - iRow);
text = text.Append(newLines);
iColumn = 0;
}
if (iColumn < xTxt)
{
string newLines = new string(' ', xTxt - iColumn);
text = text.Append(newLines);
}
text = text.Append(elValue);
iRow = yTxt;
iColumn = xTxt + elValue.Length;
}
if (lineIterator.Count != 0)
{
text = text.Append("\n\f\n");
iRow = 0;
iColumn = 0;
}
}
return text.ToString();
}
鉴于上面的xml的结构,任何想法我如何在XSLT中做同样的事情,再次将输出方法设置为文本,因此它持续存档,以便从x构建完整的行y个别单词的坐标。因此,如果对于两个单词“Hello World”,则表示为
<Text x="0" y="1">Hello</Text>
<Text x="6" y="1">World</Text>
然后“Hello”实例化一个字符串,并消耗5个字符的空格,0-4。 “世界”从6开始,因此字符索引“5”用空格填充(并且将继续这样做直到达到下一个最高的x属性)。具有相同y属性的“Text”元素中的下一个最高x属性是“6”。因此,它的值将附加到现有字符串。 y值的逻辑相同。
如果不够清楚,请告诉我,我会高兴地解释更多,或者不同。
答案 0 :(得分:0)
这比看起来要复杂一点。
<Text>
对@y
项进行分组。具有相同@y
值的所有值都在同一行。@y
值)。不代表它们更简单。@x
值排列。后者要复杂得多。@x
金额。此XSLT转换
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:key name="kLine" match="Text" use="@y" />
<xsl:variable name="padding" select="' '"/>
<xsl:template match="Data">
<xsl:for-each select="Text[generate-id() = generate-id(key('kLine', @y))]">
<xsl:apply-templates select="key('kLine', @y)">
<xsl:sort select="@x" data-type="number" />
</xsl:apply-templates>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="Text">
<xsl:if test="generate-id() = generate-id(key('kLine', @y))">
<xsl:value-of select="substring($padding, 1, @x)" />
</xsl:if>
<xsl:value-of select="." />
<xsl:if test="position() < last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
提供您的输入
First Line Continued on Same Line Still Going Slightly Indented New Line
这里使用的值得注意的技术称为Muenchian分组。
FWIW,这是一个满足您要求的解决方案。如果遇到麻烦,请自行决定。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:key name="kLine" match="Text" use="@y" />
<xsl:variable name="padX" select="' '"/>
<xsl:variable name="padY" select="'

















'"/>
<xsl:template match="Data">
<xsl:for-each select="Text[generate-id() = generate-id(key('kLine', @y))]">
<xsl:sort select="@y" data-type="number" />
<xsl:apply-templates select="key('kLine', @y)">
<xsl:sort select="@x" data-type="number" />
</xsl:apply-templates>
<!-- find the vertical position of the logically following text -->
<xsl:variable name="nextY">
<xsl:for-each select="../Text[@y > current()/@y]">
<xsl:sort select="@y" data-type="number" />
<xsl:if test="position() = 1">
<xsl:value-of select="@y" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="substring($padY, 1, $nextY - @y)" />
</xsl:for-each>
</xsl:template>
<xsl:template match="Text">
<!-- indent first item on line -->
<xsl:if test="generate-id() = generate-id(key('kLine', @y))">
<xsl:value-of select="substring($padX, 1, @x)" />
</xsl:if>
<xsl:variable name="text" select="normalize-space()" />
<!-- calculate the available text block width -->
<xsl:variable name="width">
<!-- find the horizontal position of the logically following text -->
<xsl:variable name="nextX">
<xsl:for-each select="key('kLine', @y)[@x > current()/@x]">
<xsl:sort select="@x" data-type="number" />
<xsl:if test="position() = 1">
<xsl:value-of select="@x" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:choose>
<xsl:when test="$nextX > 0">
<xsl:value-of select="$nextX - @x - 1" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="string-length($text)" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- current text + necessary right padding for next item -->
<xsl:value-of select="substring(concat($text, $padX), 1, $width)" />
<xsl:if test="position() < last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
用于此:
<Data>
<Text x="0" y="1">First Line</Text>
<Text x="20" y="1">Continued on Same Line</Text>
<Text x="42" y="1">Still Going</Text>
<Text x="4" y="5">Slightly Indented New Line</Text>
<Text x="30" y="3">30 spaces before this</Text>
</Data>
给出(为方便起见添加了一个网格数字):
| 1 2 3 4 5 | 5 0 5 0 5 0 5 0 5 0 5 -+------------------------------------------------------- 1|First Line Continued on Same Lin Still Going 2| 3| 30 spaces before this 4| 5| Slightly Indented New Line
请注意它如何删除不适合的文本。此外,这还没有经过全面测试,它只是一个概念验证。它可能会在您的输入中表现出各种有趣的内容。
另请注意,您必须决定是使用基于1的(如使用@y
)还是基于0(使用@x
)协同系统。目前,您的样本输入不一致。