2012-07-09 3 views
3

동일한 부모 (동일한 부모가 동일한 노드 이름을 공유하고 선언 된 모든 속성이 동일하다는 것을 의미 함)에서 여러 노드를 그룹화하여 "압축"해야하는 여러 유형의 XML 메시지가 있습니다. 예를 들면 :모든 속성별로 xslt 그룹화

<TopLevel CodeTL="Something"> 
    <Ratings> 
      <Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012"> 
       <RatingByNumber Code="X" Rating="10" Number="1"> 
       <RatingByNumber Code="X" Rating="19" Number="2"> 
      </Rating> 
    </Ratings> 
</TopLevel> 
    <TopLevel CodeTL="Something"> 
    <Ratings> 
      <Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012"> 
       <RatingByNumber Code="X" Rating="10" Number="1"> 
       <RatingByNumber Code="X" Rating="19" Number="2"> 
      </Rating> 
    </Ratings> 
</TopLevel> 
<TopLevel CodeTL="Something"> 
    <Ratings> 
      <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012"> 
       <RatingByNumber Code="X" Rating="10" Number="1"> 
       <RatingByNumber Code="X" Rating="19" Number="2"> 
      </Rating> 
    </Ratings> 
</TopLevel> 
<TopLevel CodeTL="Something"> 
    <Ratings> 
      <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012"> 
       <RatingByNumber Code="X" Rating="30" Number="3"> 
       <RatingByNumber Code="X" Rating="39" Number="4"> 
      </Rating> 
    </Ratings> 
</TopLevel> 

모두 동일한 CodeTL 속성과 같은 CodeA는 시작과 끝이 나는 XSLT

<TopLevel CodeTL="Something"> 
    <Ratings> 
      <Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012"> 
       <RatingByNumber Code="X" Rating="10" Number="1"> 
       <RatingByNumber Code="X" Rating="19" Number="2"> 
      </Rating> 
      <Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012"> 
       <RatingByNumber Code="X" Rating="10" Number="1"> 
       <RatingByNumber Code="X" Rating="19" Number="2"> 
      </Rating> 
      <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012"> 
       <RatingByNumber Code="X" Rating="10" Number="1"> 
       <RatingByNumber Code="X" Rating="19" Number="2"> 
       <RatingByNumber Code="X" Rating="30" Number="3"> 
       <RatingByNumber Code="X" Rating="39" Number="4"> 
      </Rating> 
    </Ratings> 
</TopLevel> 
을 사용하여 다음과 같은 출력을 생성하는 것입니다 필요하므로 속성을 마지막으로 이주를 공유하는 방법을 공지 사항

은 훨씬 깨끗하며, 소비하는 응용 프로그램에 따라 처리 시간을 절약하고 공간을 절약 할 수 있습니다.

내가 겪고있는 문제는 다른 노드 이름과 속성 (및 속성 수)이있는 xml 메시지의 유형이 다르지만 모두 여기에 표시되는 동일한 구조를 공유한다는 것입니다. 모든 것을 처리하는 것은 일반적인 방법이지만 XSLT가 제공 한 예제를 변형하여 보내 주신 모든 XML 메시지에 대한 사용자 지정 코드를 만들 수있게되어서 고맙겠습니다.

답변

1

이 일반 XSLT 2.0 변환 :

<t> 
    <TopLevel CodeTL="Something"> 
     <Ratings> 
       <Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012"> 
        <RatingByNumber Code="X" Rating="10" Number="1"/> 
        <RatingByNumber Code="X" Rating="19" Number="2"/> 
       </Rating> 
     </Ratings> 
    </TopLevel> 
     <TopLevel CodeTL="Something"> 
     <Ratings> 
       <Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012"> 
        <RatingByNumber Code="X" Rating="10" Number="1"/> 
        <RatingByNumber Code="X" Rating="19" Number="2"/> 
       </Rating> 
     </Ratings> 
    </TopLevel> 
    <TopLevel CodeTL="Something"> 
     <Ratings> 
       <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012"> 
        <RatingByNumber Code="X" Rating="10" Number="1"/> 
        <RatingByNumber Code="X" Rating="19" Number="2"/> 
       </Rating> 
     </Ratings> 
    </TopLevel> 
    <TopLevel CodeTL="Something"> 
     <Ratings> 
       <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012"> 
        <RatingByNumber Code="X" Rating="30" Number="3"/> 
        <RatingByNumber Code="X" Rating="39" Number="4"/> 
       </Rating> 
     </Ratings> 
    </TopLevel> 
</t> 

(잘 형성된 XML 문서가 단일 최상위 요소로 래핑) 제공된 XML 적용

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:my="my:my" exclude-result-prefixes="xs my"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:template match="/*"> 
    <t> 
     <xsl:sequence select="my:grouping(*)"/> 
    </t> 
</xsl:template> 

<xsl:function name="my:grouping" as="node()*"> 
    <xsl:param name="pElems" as="element()*"/> 

    <xsl:if test="$pElems"> 
     <xsl:for-each-group select="$pElems" group-by="my:signature(.)"> 
     <xsl:copy> 
      <xsl:copy-of select="@*"/> 

      <xsl:sequence select="my:grouping(current-group()/*)"/> 
     </xsl:copy> 
     </xsl:for-each-group> 
    </xsl:if> 
</xsl:function> 

<xsl:function name="my:signature" as="xs:string"> 
    <xsl:param name="pElem" as="element()"/> 

    <xsl:variable name="vsignAttribs" as="xs:string*"> 
     <xsl:for-each select="$pElem/@*"> 
     <xsl:sort select="name()"/> 

     <xsl:value-of select="concat(name(), '=', .,'|')"/> 
     </xsl:for-each> 
    </xsl:variable> 

    <xsl:sequence select= 
    "concat(name($pElem), '|', string-join($vsignAttribs, ''))"/> 
</xsl:function> 
</xsl:stylesheet> 

은 원하는 정확한 결과를 생성합니다. :

,363,210
<t> 
    <TopLevel CodeTL="Something"> 
     <Ratings> 
     <Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012"> 
      <RatingByNumber Code="X" Rating="10" Number="1"/> 
      <RatingByNumber Code="X" Rating="19" Number="2"/> 
     </Rating> 
     <Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012"> 
      <RatingByNumber Code="X" Rating="10" Number="1"/> 
      <RatingByNumber Code="X" Rating="19" Number="2"/> 
     </Rating> 
     <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012"> 
      <RatingByNumber Code="X" Rating="10" Number="1"/> 
      <RatingByNumber Code="X" Rating="19" Number="2"/> 
      <RatingByNumber Code="X" Rating="30" Number="3"/> 
      <RatingByNumber Code="X" Rating="39" Number="4"/> 
     </Rating> 
     </Ratings> 
    </TopLevel> 
</t> 

설명는 :

  1. 수행 된 그룹화 기능 my:grouping() 구현 재귀이다.

  2. 최상위 요소는 레벨이 단일이며 자체의 단순 복사본이 아닌 다른 그룹화가 필요하지 않습니다. 그런 다음이 얕은 복사본의 본문 내에서 하위 수준의 그룹화는 my:grouping() 함수에 의해 수행됩니다.

  3. 함수 my:grouping()에는 하나의 인수가 있습니다.이 인수는 바로 위의 그룹에있는 모든 요소의 하위 요소입니다. 현재 수준의 모든 그룹을 반환합니다.

  4. 그들의 서명에 기초하여 그룹화 기능을 인수로 전달 요소의 시퀀스 - 모든 이름 값의 속성의 쌍과 해당 값 요소 이름의 연결 및 이들 적절한 분리 문자를 사용하여 구분됩니다. 요소의 서명은 함수 my:signature()에 의해 생성됩니다.


    II

. 일반 XSLT 1.0 용액이 변형 (위) 같은 XML 문서에 적용

<xsl:stylesheet version="1.0" 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
     xmlns:ext="http://exslt.org/common" 
     xmlns:my="my:my" exclude-result-prefixes="my ext"> 
     <xsl:output omit-xml-declaration="yes" indent="yes"/> 
     <xsl:strip-space elements="*"/> 

     <xsl:variable name="vrtfPass1"> 
      <xsl:apply-templates select="/*"/> 
     </xsl:variable> 

     <xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)"/> 

     <xsl:template match="/"> 
      <xsl:apply-templates select="$vPass1/*" mode="pass2"/> 
     </xsl:template> 

     <xsl:template match="/*" mode="pass2"> 
      <xsl:copy> 
       <xsl:call-template name="my:grouping"> 
       <xsl:with-param name="pElems" select="*"/> 
       </xsl:call-template> 
      </xsl:copy> 
     </xsl:template> 

     <xsl:template name="my:grouping"> 
      <xsl:param name="pElems" select="/.."/> 

      <xsl:if test="$pElems"> 
      <xsl:for-each select="$pElems"> 
       <xsl:variable name="vPos" select="position()"/> 

       <xsl:if test= 
       "not(current()/@my:sign 
        = $pElems[not(position() >= $vPos)]/@my:sign 
        )"> 

       <xsl:element name="{name()}"> 
        <xsl:copy-of select="namespace::*[not(. = 'my:my')]"/> 
        <xsl:copy-of select="@*[not(name()='my:sign')]"/> 
        <xsl:call-template name="my:grouping"> 
        <xsl:with-param name="pElems" select= 
        "$pElems[@my:sign = current()/@my:sign]/*"/> 
        </xsl:call-template> 
       </xsl:element> 
       </xsl:if> 

      </xsl:for-each> 
      </xsl:if> 
     </xsl:template> 

    <xsl:template match="/*"> 
      <xsl:copy> 
       <xsl:apply-templates/> 
      </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*/*"> 
     <xsl:variable name="vSignature"> 
     <xsl:call-template name="signature"/> 
     </xsl:variable> 
     <xsl:copy> 
     <xsl:copy-of select="@*"/> 
     <xsl:attribute name="my:sign"> 
     <xsl:value-of select="$vSignature"/> 
     </xsl:attribute> 

     <xsl:apply-templates/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template name="signature"> 
     <xsl:variable name="vsignAttribs"> 
     <xsl:for-each select="@*"> 
      <xsl:sort select="name()"/> 

       <xsl:value-of select="concat(name(), '=', .,'|')"/> 
      </xsl:for-each> 
     </xsl:variable> 

     <xsl:value-of select= 
      "concat(name(), '|', $vsignAttribs)"/> 
    </xsl:template> 
</xsl:stylesheet> 

, 다시 같은 올바른 결과 생산된다

<t> 
    <TopLevel> 
     <Ratings> 
     <Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012"> 
      <RatingByNumber Code="X" Rating="10" Number="1"/> 
      <RatingByNumber Code="X" Rating="19" Number="2"/> 
     </Rating> 
     <Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012"> 
      <RatingByNumber Code="X" Rating="10" Number="1"/> 
      <RatingByNumber Code="X" Rating="19" Number="2"/> 
     </Rating> 
     <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012"> 
      <RatingByNumber Code="X" Rating="10" Number="1"/> 
      <RatingByNumber Code="X" Rating="19" Number="2"/> 
      <RatingByNumber Code="X" Rating="30" Number="3"/> 
      <RatingByNumber Code="X" Rating="39" Number="4"/> 
     </Rating> 
     </Ratings> 
    </TopLevel> 
</t> 

설명 :

  1. 이것은 2 패스 변환입니다.

  2. 모든 요소에 대한 첫 번째 패스에서 서명이 계산되고 새 속성 my:sign의 값어치가됩니다.

  3. 동일한 재귀 그룹화 알고리즘이 XSLT 2.0 솔루션과 함께 사용됩니다.

+0

그건 내가 원했던 것 같아. 나는 1.0으로 물러났다. 나는 그것에 대해 뭔가 할 수 있는지 보겠습니다. 상세한 답변을 해줘서 고맙습니다. –

+1

@EdFox : XSLT 1.0에서는 동일한 개념이 사용되지만 첫 번째 단계에서는 각 요소의 복사본을 만들고 서명이 포함 된 특수한 새 요소 (또는 특성)를 추가하는 2 단계 변환을 사용합니다. 두 번째 단계에서 우리는이 특별한 요소/속성에 대해 간단한 Muenchian 그룹화를 수행합니다. –

+0

게시물에 1.0 버전을 추가 할 수 있습니까? 나는 아직도 xslt에 압도되어서 미안하다. 나는 당신의 설명을 실제 코드로 번역 할 수 있다고 확신하지 못한다. –

1

이 1.0 XSLT 스타일 시트는 원하는 결과를 산출 :

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:key name="byCodeTL" match="TopLevel" use="@CodeTL"/> 
    <xsl:key name="byAttrs" match="Rating" 
      use="concat(../../@CodeTL, '|', @CodeA, '|', @Start, '|', @End)"/> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="TopLevel[generate-id()= 
            generate-id(key('byCodeTL', @CodeTL)[1])]"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <Ratings> 
       <xsl:apply-templates 
         select="key('byCodeTL', @CodeTL)/Ratings/*"/> 
      </Ratings> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="Rating[generate-id()= 
           generate-id(key('byAttrs', 
      concat(../../@CodeTL, '|', @CodeA, '|', @Start, '|', @End))[1])]"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|key('byAttrs', 
       concat(../../@CodeTL, '|', @CodeA, '|', @Start, '|', @End))/*"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="TopLevel"/> 
    <xsl:template match="Rating"/> 
</xsl:stylesheet> 

TopLevel 모든 요소들이 CodeTL 속성에 의해 그룹화된다. 모든 Rating 요소는 해당 속성과 CodeTL 속성의 조합으로 해당 TopLevel의 그룹화됩니다.

+0

다른 코드를 가진 2 개의 TopLevel 노드가 있지만 파일에 나타나는 첫 번째 노드 아래에 그룹화 된 동일한 하위 노드가있는 경우 실패합니다. 예 : http://pastebin.com/0EPpnycL –

+0

@EdFox - 좋은 지적입니다. 우리는'Rating' 그룹 키에 조부모'@ CodeTL '을 포함시켜야합니다. 내 편집을 참조하십시오. –

관련 문제