2017-11-13 3 views
2

노드 크기, 속성 이름, 속성 값 및 마지막으로 노드 값을 기준으로 4 차원을 기반으로 xml 파일을 정렬하려고합니다.이름, 속성 이름, 속성 값 및 노드 값을 기준으로 XML 정렬

내 XML

<NodeRoot> 
    <NodeA class="3"> 
     <NodeB> 
      <NodeC abc="1">103</NodeC> 
      <NodeD>103</NodeD> 
      <NodeC pqr="2">101</NodeC> 
      <NodeC pqr="1">102</NodeC> 
      <NodeD>101</NodeD> 
     </NodeB> 
    </NodeA> 
    <NodeA class="1"> 
     <NodeGroup> 
      <NodeC name="z" asc="2">103</NodeC> 
      <NodeC name="b">101</NodeC> 
      <NodeC name="a">102</NodeC> 
     </NodeGroup> 
    </NodeA> 
</NodeRoot> 

내 XSL

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output encoding="utf-8" method="xml" omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 
<xsl:template match="@*|node()"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*"> 
      <xsl:sort select="local-name()"/> 
      <xsl:sort select="."/> 
     </xsl:apply-templates> 
     <xsl:apply-templates select="node()"> 
      <xsl:sort select="local-name()"/> 
      <xsl:sort select="."/> 
     </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 
</xsl:stylesheet> 

전류 출력

<NodeRoot> 
    <NodeA class="1"> 
     <NodeGroup> 
     <NodeC name="b">101</NodeC> 
     <NodeC name="a">102</NodeC> 
     <NodeC asc="2" name="z">103</NodeC> 
     </NodeGroup> 
    </NodeA> 
    <NodeA class="3"> 
     <NodeB> 
     <NodeC pqr="2">101</NodeC> 
     <NodeC pqr="1">102</NodeC> 
     <NodeC abc="1">103</NodeC> 
     <NodeD>101</NodeD> 
     <NodeD>103</NodeD> 
     </NodeB> 
    </NodeA> 
</NodeRoot> 

예상 결과

<NodeRoot> 
    <NodeA class="1"> 
     <NodeGroup> 
     <NodeC asc="2" name="z">103</NodeC> 
     <NodeC name="a">102</NodeC> 
     <NodeC name="b">101</NodeC> 
     </NodeGroup> 
    </NodeA> 
    <NodeA class="3"> 
     <NodeB> 
     <NodeC abc="1">103</NodeC> 
     <NodeC pqr="1">102</NodeC> 
     <NodeC pqr="2">101</NodeC> 
     <NodeD>101</NodeD> 
     <NodeD>103</NodeD> 
     </NodeB> 
    </NodeA> 
</NodeRoot> 

테스트 XSLT ->http://xsltransform.net/naZXpY7

+0

'NodeGroup/NodeC' 요소가'name' 속성의'a','b','z' 값에 따라 정렬되므로 원하는 결과가 속성 값에 따라 어떻게 정렬되는지 알 수 있습니다. 또한,'NodeA' 엘리먼트는'class' 애트리뷰트의'1','3' 값에 의해 정렬됩니다. 'NodeB/NodeC' 요소 (속성 없음)가'101','103' 값으로 정렬되기 때문에 노드 값별로 정렬되는 방법을 볼 수 있습니다. 그러나 출력이 노드 이름 또는 속성 이름별로 어떻게 정렬되는지는 알 수 없습니다. –

+1

요소에 둘 이상의 속성이있는 경우 어떻게 될 것으로 예상됩니까? –

+0

@BenL - 더 나은 샘플 xml로 질문 업데이트 –

답변

1

당신은 지역의 이름과 값, 그리고 (다시 로컬 이름과 문자열 값으로) 모든 아이들 요소의 모든 속성을 분류하고 .

지금까지 그렇게 좋았습니다.

당신이 직면하는 어려움 중 하나는 "속성 이름"을 기준으로 정렬한다는 것입니다. 당신이 알파벳 순서로 자신의 속성 이름의 목록으로 분류 요소를 원하는 것처럼 당신의 노드 그룹 요소의 자식에 대한 정렬 키

'NodeC', 'asc name', '2 z', 103 
'NodeC', 'name', 'a', 102 
'NodeC', 'name', 'b', 201 

것을 다음 어려움이 전혀 거기되도록 귀하의 예제에서, 그것은 보지 않는다 컨텍스트 노드로 NodeGroup 요소의 첫 번째 NodeC를 사용하여 XPath 1.0 표현식에서 'asc name'값을 가져 오는 확실한 방법입니다. 물론 문자열을 생성 할 수도 있지만 명명 된 템플릿을 호출해야합니다. (또는, 더 정확하게는 : 나는 그런 전화없이 생성하는 방법을 볼 수 없습니다.)

XSLT 2.0 솔루션 문제는 XSLT 2.0의 비교적 간단

;

<xsl:template match="@*|node()"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*"> 
     <xsl:sort select="local-name()"/> 
     <xsl:sort select="."/> 
    </xsl:apply-templates> 
    <xsl:apply-templates select="node()"> 
     <xsl:sort select="local-name()"/> 
     <xsl:sort select="string-join(local:key2(.), ' ')"/> 
     <xsl:sort select="string-join(local:key3(.), ' ')"/> 
     <xsl:sort select="." data-type="number"/> 
    </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 

<xsl:function name="local:key2" as="xs:string*"> 
    <xsl:param name="e" as="node()"/> 
    <xsl:for-each select="$e/@*"> 
    <xsl:sort select="local-name()"/> 
    <xsl:sort select="string()"/> 
    <xsl:value-of select="local-name()"/> 
    </xsl:for-each> 
</xsl:function> 

<xsl:function name="local:key3" as="xs:string*"> 
    <xsl:param name="e" as="node()"/> 
    <xsl:for-each select="$e/@*"> 
    <xsl:sort select="local-name()"/> 
    <xsl:sort select="string()"/> 
    <xsl:value-of select="string()"/> 
    </xsl:for-each> 
</xsl:function> 

이러한 일반적인 접근법은 또한 사용자 정의 기능에 대한 EXSLT 확장자 XSLT 1.0에서 사용될 수있다 : 다음 단편 중요한 비트를 나타낸다. EXSLT 기능

당신의 XSLT의 경우와 XSLT 1.0

솔루션 1.0 프로세서가 EXSLT 스타일의 사용자 정의 기능을 지원합니다, 당신은 XSLT 1.0에서 비슷한 일을 할 수 있습니다. (내 최초의 시도는 실패하지만 스타일 시트 요소에 extension-element-prefixes 속성을 추가 기억 할 때 오류가 사라졌다.)

xsltproc하여 입력에서 실행
<xsl:stylesheet version="1.0" 
       extension-element-prefixes="func" 
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       xmlns:xs="http://www.w3.org/2001/XMLSchema" 
       xmlns:func="http://exslt.org/functions" 
       xmlns:local="http://example.com/nss/dummy"> 

    <xsl:output encoding="utf-8" 
       method="xml" 
       omit-xml-declaration="yes" 
       indent="yes"/> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="@*|node()"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*"> 
     <xsl:sort select="local-name()"/> 
     <xsl:sort select="."/> 
     </xsl:apply-templates> 
     <xsl:apply-templates select="node()"> 
     <xsl:sort select="local-name()"/> 
     <xsl:sort select="local:key2(.)"/> 
     <xsl:sort select="local:key3(.)"/> 
     <xsl:sort select="." data-type="number"/> 
     </xsl:apply-templates> 
    </xsl:copy> 
    </xsl:template> 

    <func:function name="local:key2"> 
    <xsl:param name="e" select="."/> 

    <func:result> 
     <xsl:for-each select="$e/@*"> 
     <xsl:sort select="local-name()"/> 
     <xsl:sort select="string()"/> 
     <xsl:value-of select="concat(local-name(), ' ')"/> 
     </xsl:for-each> 
    </func:result> 
    </func:function> 

    <func:function name="local:key3"> 
    <xsl:param name="e" select="."/> 
    <func:result> 
     <xsl:for-each select="$e/@*"> 
     <xsl:sort select="local-name()"/> 
     <xsl:sort select="string()"/> 
     <xsl:value-of select="concat(string(), ' ')"/> 
     </xsl:for-each> 
    </func:result> 
    </func:function> 
</xsl:stylesheet> 

이 원하는 출력을 생성합니다.

노드 집합 확장을 사용하여 XSLT 1.0에서 영리한 작업을 수행 할 수도 있습니다.

확장되지 않은 XSLT 1의 2 단계 파이프 라인.0

그러나 확장되지 않은 XSLT 1.0에서이 문제를 해결하는 가장 간단한 방법은 두 개의 스타일 시트를 파이프 라인으로 연결하는 것입니다. 첫 번째 정렬 키가 2, 3 제공하는 모든 요소에 두 개의 속성을 추가한다 (그들이 당신이 원하는 것을 할 수 있도록 명명 된 템플릿을 조정합니다.) 난을 줄이기 위해 네임 스페이스에 넣어 한

<xsl:stylesheet version="1.0" 
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       xmlns:PJ="http://example.com/PankajJaju"> 
    <xsl:output encoding="utf-8" 
       method="xml" 
       omit-xml-declaration="yes" 
       indent="yes"/> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="@*|node()"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*"/> 
     <xsl:if test="self::*"> 
     <xsl:attribute name="PJ:attribute-names" 
         namespace="http://example.com/PankajJaju"> 
      <xsl:call-template name="attribute-name-list"/> 
     </xsl:attribute> 
     <xsl:attribute name="PJ:attribute-values" 
         namespace="http://example.com/PankajJaju"> 
      <xsl:call-template name="attribute-value-list"/> 
     </xsl:attribute> 
     </xsl:if> 
     <xsl:apply-templates select="node()"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template name="attribute-name-list"> 
    <xsl:for-each select="@*"> 
     <xsl:sort select="local-name()"/> 
     <xsl:sort select="string()"/> 
     <xsl:value-of select="concat(local-name(), ' ')"/> 
    </xsl:for-each> 
    </xsl:template> 
    <xsl:template name="attribute-value-list"> 
    <xsl:for-each select="@*"> 
     <xsl:sort select="local-name()"/> 
     <xsl:sort select="string()"/> 
     <xsl:value-of select="concat(string(), ' ')"/> 
    </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

이름 충돌의 가능성.

두 번째 것은 정렬 키를 사용하여 실제 정렬을 수행하고 임시 속성을 억제합니다.

<xsl:stylesheet version="1.0" 
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       xmlns:PJ="http://example.com/PankajJaju"> 
    <xsl:output encoding="utf-8" 
       method="xml" 
       omit-xml-declaration="yes" 
       indent="yes"/> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="@*|node()"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*"> 
     <xsl:sort select="local-name()"/> 
     <xsl:sort select="."/> 
     </xsl:apply-templates> 
     <xsl:apply-templates select="node()"> 
     <xsl:sort select="local-name()"/> 
     <xsl:sort select="@PJ:attribute-names"/> 
     <xsl:sort select="@PJ:attribute-values"/> 
     <xsl:sort select="."/> 
     </xsl:apply-templates> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="@PJ:attribute-names | @PJ:attribute-values"/> 
</xsl:stylesheet> 

이들은 원하는 기술을 사용하여 파이프 라인 될 수 있습니다. ...

xsltproc p1.xsl input.xml | xsltproc p2.xsl - 

을 예를 들어, bash는 명령 줄에서 xsltproc을 사용하고, 파이프 라인 스타일 1과 2에 이름 p1.xsl 및 p2.xsl을 할당 이것은 당신이 싶은 말은 출력을 생성합니다.

+0

XSLT 2.0에서 오류가 발생하지만 XSLT 1.0이 작동했습니다. 당신의 2 부분 해결책은 독창적입니다. 하나의 작은 문제,'exclude-result-prefixes = "PJ"를 추가하여 루트 노드의 네임 스페이스를 억제했지만 작동하지 않습니다. –

+0

'exclude-result-prefixes'는 리터럴 결과 요소에만 적용됩니다. 'xsl : copy' 명령으로 생성 된 출력에 포함 된 네임 스페이스에는 영향을 미치지 않습니다. –

+0

그건 전혀 신경 쓰이지 않는 작은 문제입니다. 솔루션을 가져 주셔서 감사합니다. –

0

@C. M. Sperberg-McQueen의 노드 집합 확장에 대한 제안과 https://www.xml.com/pub/a/2003/07/16/nodeset.html에있는 예제의 도움으로 McQueen의 2 xsls를 병합하는 단일 xsl을 만들었습니다.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" exclude-result-prefixes="exslt" xmlns:PJ="http://example.com/PankajJaju"> 
    <xsl:output encoding="utf-8" method="xml" omit-xml-declaration="yes" indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="node()|@*" name="first-pass"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <xsl:if test="self::*"> 
       <xsl:attribute name="PJ:attribute-names" namespace="http://example.com/PankajJaju"> 
        <xsl:call-template name="attribute-name-list"/> 
       </xsl:attribute> 
       <xsl:attribute name="PJ:attribute-values" namespace="http://example.com/PankajJaju"> 
        <xsl:call-template name="attribute-value-list"/> 
       </xsl:attribute> 
      </xsl:if> 
      <xsl:apply-templates select="node()"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template name="attribute-name-list"> 
     <xsl:for-each select="@*"> 
      <xsl:sort select="local-name()"/> 
      <xsl:sort select="string()"/> 
      <xsl:value-of select="concat(local-name(), ' ')"/> 
     </xsl:for-each> 
    </xsl:template> 
    <xsl:template name="attribute-value-list"> 
     <xsl:for-each select="@*"> 
      <xsl:sort select="local-name()"/> 
      <xsl:sort select="string()"/> 
      <xsl:value-of select="concat(string(), ' ')"/> 
     </xsl:for-each> 
    </xsl:template> 

    <xsl:template match="/"> 
     <xsl:variable name="process-one"> 
      <xsl:call-template name="first-pass"/> 
     </xsl:variable> 
     <xsl:apply-templates select="exslt:node-set($process-one)" mode="second-pass"/> 
    </xsl:template> 

    <xsl:template match="@*|node()" mode="second-pass"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()|@*" mode="second-pass"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="*" mode="second-pass"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*" mode="second-pass"> 
       <xsl:sort select="local-name()"/> 
       <xsl:sort select="."/> 
      </xsl:apply-templates> 
      <xsl:apply-templates select="node()" mode="second-pass"> 
       <xsl:sort select="local-name()"/> 
       <xsl:sort select="@PJ:attribute-names"/> 
       <xsl:sort select="@PJ:attribute-values"/> 
       <xsl:sort select="."/> 
      </xsl:apply-templates> 
     </xsl:copy> 
    </xsl:template>   
    <xsl:template match="@PJ:attribute-names | @PJ:attribute-values" mode="second-pass"/> 
</xsl:stylesheet>