2014-03-05 68 views
2

我完全不熟悉遊戲。我正在處理的項目需要能夠交替呈現爲html或文本。我決定嘗試使用XSLT,這樣我就可以在不改變底層代碼結構的情況下修改輸出。 HTML輸出很好。在嘗試向文本寫入轉換時遇到一些小問題。這裏的原因:將XML轉換爲格式化文本樣本瓶XSLT

我轉換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」值的文本元素串(或座標),這正是我所需要的文本輸出做這樣從什麼樣的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> 

然後「你好」實例化一個字符串,並且消耗的空間5個字符,0-4。 「世界」從6開始,因此字符索引「5」中充滿了空格(並且會繼續這樣做直到達到下一個最高的x屬性)。具有相同y屬性的'Text'元素中的下一個最高x屬性是「6」。所以它的值被附加到現有的字符串。 y值的邏輯相同。

請讓我知道如果這不夠清楚,我會愉快地解釋更多,或不同的解釋。

+0

你能澄清你想從文本輸出中得到什麼嗎?聽起來你需要HTML和文本的不同模式,因爲它們有不同的要求。 – harpo

+0

是的,完全正確。我將修改帖子以反映說明。 – Joe

+0

所以你實際上需要在文本模式下「協調」定位所有單獨的位? – Tomalak

回答

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>&#xA;</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() &lt; 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="'&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;'"/> 

    <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 &gt; 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 &gt; 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 &gt; 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() &lt; 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)共同定位系統。目前在您的示例輸入中不一致。