2016-08-19 79 views
0

我有一個XML文檔,必須根據節點的值進行過濾,然後必須以JSON格式返回匹配節點的父節點及其所有子節點。基於節點值過濾XML並使用XSLT轉換爲JSON

XML:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <col-name name="col1Name" /> 
    <col-name name="col2Name" /> 
    <row> 
     <col>Test1</col> 
     <col>Test2</col> 
    </row> 
    <row> 
     <col>Test3</col> 
     <col>Test4</col> 
    </row> 
</root> 

XSL:

<?xml version="1.0" encoding="UTF-8" ?> 
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> 
    <xsl:output method="text" /> 
    <xsl:template match="/"> 
     { 
      "filtered-result": [ 
       <xsl:for-each select="//row[col[normalize-space(text()) = 'Test2']]"> 
        <xsl:variable name="columnCount" select="count(./*)" /> 
        { 
         <xsl:for-each select="./*"> 
          <xsl:variable name="columnIndex" select="position()" /> 
          "<xsl:value-of select="normalize-space(//col-name[$columnIndex]/@name)" />": "<xsl:value-of select="." />"<xsl:if test="$columnIndex &lt; $columnCount">,</xsl:if> 
         </xsl:for-each> 
        }<xsl:if test="./following-sibling::*">,</xsl:if> 
       </xsl:for-each> 
      ] 
     } 
    </xsl:template> 
</xsl:transform> 

輸出:

{ 
    "filtered-result": [ 

      { 

        "col1Name": "Test1", 
        "col2Name": "Test2" 
      } 
    ] 
} 

我使用SAXON作爲XSLT PR ocessor。

我正在按預期得到結果。有沒有其他方法可以獲得理想的結果?建議感激。

+0

的哪個版本撒克遜你在用嗎?對XSLT和XPath 3.0/3.1中的XML到JSON和JSON序列化有一些新的支持,因此您可以使用9.6或9.7版本,具體取決於版本和版本。 –

+0

@MartinHonnen我正在使用'Saxon HE 9.6.0.7'。我必須研究Xpath 3.0的新功能。 – Beginner

回答

3

我試圖簡化你的代碼

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> 
    <xsl:output method="text" /> 

    <xsl:key name="col" match="col-name"> 
     <xsl:number/> 
    </xsl:key> 

    <xsl:template match="/"> 
     { 
     "filtered-result": [ 
     <xsl:for-each select="//row[col[normalize-space(.) = 'Test2']]"> 
      { 
      <xsl:for-each select="*"> 
       "<xsl:value-of select="key('col', string(position()))/@name" />": "<xsl:value-of select="." />"<xsl:if test="position() lt last()">,</xsl:if> 
      </xsl:for-each> 
      }<xsl:if test="position() lt last()">,</xsl:if> 
     </xsl:for-each> 
     ] 
     } 
    </xsl:template> 
</xsl:transform> 

無論您的解決方案,以及我在簡化試圖從問題的困擾,在這將需要轉義的JSON值,就像一列的任何字符雙引號"會破壞輸出結果。

因此,我認爲最好依靠考慮此類問題的轉換,並根據需要轉義任何字符。運行XSLT 3.0時

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:math="http://www.w3.org/2005/xpath-functions/math" 
    xmlns:map="http://www.w3.org/2005/xpath-functions/map" 
    xmlns:array="http://www.w3.org/2005/xpath-functions/array" 
    exclude-result-prefixes="xs math map array" 
    version="3.0"> 

    <xsl:output method="json" indent="yes"/> 

    <xsl:key name="col" match="col-name"> 
     <xsl:number/> 
    </xsl:key> 

    <xsl:template match="root"> 
     <xsl:sequence select="map { 'filtered-result' : array:join(row[col = 'Test2']/[map:merge(col/map:entry(key('col', string(position()))/@name, string())) ]) }"/> 
    </xsl:template> 

</xsl:stylesheet> 

撒克遜9.7 EE,:

在XSLT 3.0使用XPath 3.1的支持,你可以構建mapsarrays,您可以serialize them as JSON,這是我嘗試用你的問題這些功能相對於輸入樣本代碼

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <col-name name="col1Name" /> 
    <col-name name="col2Name" /> 
    <row> 
     <col>Test1</col> 
     <col>Test2</col> 
    </row> 
    <row> 
     <col>Test3</col> 
     <col>Test4</col> 
    </row> 
    <row> 
     <col>Test5</col> 
     <col>Test2"</col> 
    </row> 
</root> 

輸出

{ 
    "filtered-result": [ 
    { 
     "col1Name":"Test1", 
     "col2Name":"Test2" 
    }, 
    { 
     "col1Name":"Test5", 
     "col2Name":"Test2\"" 
    } 
    ] 
} 

作爲第三個選項,還需要XSLT 3.0,但可在撒克遜9.7 HE您可以將您的XML輸入該功能所需的input format後使用的功能xml-to-jsonhttps://www.w3.org/TR/xslt-30/#func-xml-to-json):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/2005/xpath-functions" 
    exclude-result-prefixes="xs" version="3.0"> 

    <xsl:output method="text"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:key name="col" match="col-name"> 
     <xsl:variable name="index" as="xs:integer"> 
      <xsl:number/> 
     </xsl:variable> 
     <xsl:sequence select="$index"/> 
    </xsl:key> 

    <xsl:template match="/"> 
     <xsl:variable name="json-doc"> 
      <xsl:apply-templates/> 
     </xsl:variable> 
     <xsl:value-of select="xml-to-json($json-doc, map{ 'indent': true()})"/> 
    </xsl:template> 

    <xsl:template match="root"> 
     <map> 
      <array key="filtered-results"> 
       <xsl:apply-templates select="row[col[normalize-space(.) = 'Test2']]"/> 
      </array> 
     </map> 
    </xsl:template> 

    <xsl:template match="row"> 
     <map> 
      <xsl:apply-templates/> 
     </map> 
    </xsl:template> 

    <xsl:template match="col"> 
     <string key="{key('col', position())/@name}"> 
      <xsl:value-of select="."/> 
     </string> 
    </xsl:template> 

</xsl:stylesheet> 
+0

非常感謝:) – Beginner