以文本方式查看主题

-  中文XML论坛 - 专业的XML技术讨论区  (http://bbs.xml.org.cn/index.asp)
--  『 XSL/XSLT/XSL-FO/CSS 』  (http://bbs.xml.org.cn/list.asp?boardid=8)
----  请教一个问题,高手请进  (http://bbs.xml.org.cn/dispbbs.asp?boardid=8&rootid=&id=10019)


--  作者:cgp1996
--  发布时间:9/15/2004 11:48:00 PM

--  请教一个问题,高手请进
<?xml version="1.0"?>
<character>
<aa ID="1000" name="one">
  <aa1 ID="0100">
    <aaa1 ID="0100" name="two"/>
    <aaa2 ID="1000"/>
  </aa1>
  <aa2 ID="0010"/>
  <aa3 ID="1000"/>
</aa>
<bb ID="0010">
  <bb1 ID="0010" name="three"/>
  <bb2 ID="0001"/>
  <bb3 ID="0000"/>
</bb>
</character>
转换规则为:
1.根据ID值对元素进行分类:1000-A 0100-B 0010-C 0001-D
2.不改变直接父子关系,如aa属于A,aa3也属于A,转换后aa3仍为aa的子元素,
但是aaa2就转换为aa的兄弟元素
3.若一个元素不属于任一类,则与其父元素同类,若父元素也不属于任一类,则不仅进行处理,即删掉它
4.其它属性值不变,如name
转换后的文档为
<?xml version="1.0"?>
<CHAR>
<A>
  <aa ID="1000" name="one">
    <aa3 ID="1000"/>
  </aa>
  <aaa2 ID="1000"/>
</A>
<B>
  <aa1 ID="0100">
    <aaa1 ID="0100" name="two"/>
  </aa1>
</B>
<C>
  <aa2 ID="0010"/>
  <bb ID="0010">
    <bb1 ID="0010" name="three"/>
    <bb3 ID="0000"/>
  </bb>
</C>
<D>
  <bb2 ID="0001"/>
</D>
</CHAR>
谢谢了
--  作者:doubleG
--  发布时间:9/16/2004 4:58:00 PM

--  
思路:如果ID属性满足但父节点的ID属性不为当前的值(1000,0100,0010,0001)中的一个,打出当前节点,并打出满足要求的直接子节点;如果不为当前的值,跳过。xmlspy2004下通过。可能写的稍有麻烦,呵呵。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
 <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
 <xsl:variable name="OrderOne">1000</xsl:variable>
 <xsl:variable name="OrderTwo">0100</xsl:variable>
 <xsl:variable name="OrderThree">0010</xsl:variable>
 <xsl:variable name="OrderFour">0001</xsl:variable>
 <xsl:template match="/">
 <xsl:element name="Char">
  <xsl:call-template name="ReOrder">
   <xsl:with-param name="IDName" select="$OrderOne"/>
   <xsl:with-param name="OrderName">A</xsl:with-param>
  </xsl:call-template>
  <xsl:call-template name="ReOrder">
   <xsl:with-param name="IDName" select="$OrderTwo"/>
   <xsl:with-param name="OrderName">B</xsl:with-param>
  </xsl:call-template>
  <xsl:call-template name="ReOrder">
   <xsl:with-param name="IDName" select="$OrderThree"/>
   <xsl:with-param name="OrderName">C</xsl:with-param>
  </xsl:call-template>
  <xsl:call-template name="ReOrder">
   <xsl:with-param name="IDName" select="$OrderFour"/>
   <xsl:with-param name="OrderName">D</xsl:with-param>
  </xsl:call-template>
 </xsl:element>
 </xsl:template>
 
 <xsl:template name="ReOrder">
  <xsl:param name="IDName"/>
  <xsl:param name="OrderName"/>
  <xsl:element name="{$OrderName}">
   <xsl:for-each select="//node()[@ID=$IDName]">
    <xsl:if test="(../@ID!=$IDName) or (name(..)='character') ">
     <xsl:call-template name="SetCurrentNode">
      <xsl:with-param name="IDName" select="$IDName"/>
      <xsl:with-param name="OrderName" select="$OrderName"/>
      <xsl:with-param name="currentNode" select="."/>
     </xsl:call-template>
     
    </xsl:if>
   </xsl:for-each>
  </xsl:element>
 </xsl:template>
 <xsl:template name="SetCurrentNode">
  <xsl:param name="IDName"/>
  <xsl:param name="OrderName"/>
  <xsl:param name="currentNode"/>
  <xsl:element name="{name($currentNode)}">
   <xsl:for-each select="$currentNode/attribute::*">
    <xsl:attribute name="{name(.)}"><xsl:value-of select="."/></xsl:attribute>
   </xsl:for-each>
   <xsl:if test="@ID=$IDName">
    <xsl:call-template name="SetChildrenNode">
      <xsl:with-param name="IDName" select="$IDName"/>
      <xsl:with-param name="OrderName" select="$OrderName"/>
      <xsl:with-param name="currentNode" select="."/>
     </xsl:call-template>
   </xsl:if>
  </xsl:element>
 </xsl:template>
 <xsl:template name="SetChildrenNode">
  <xsl:param name="IDName"/>
  <xsl:param name="OrderName"/>
  <xsl:param name="currentNode"/>
  <xsl:if test="count($currentNode/child::*)!=0">
   <xsl:for-each select="$currentNode/child::*">
    <xsl:choose>
     <xsl:when test="@ID=$IDName">
      <xsl:call-template name="SetCurrentNode">
       <xsl:with-param name="currentNode" select="."/>
      </xsl:call-template>
      <xsl:call-template name="SetChildrenNode">
       <xsl:with-param name="IDName" select="$IDName"/>
       <xsl:with-param name="OrderName" select="$OrderName"/>
       <xsl:with-param name="currentNode" select="."/>
      </xsl:call-template>
     </xsl:when>
     <xsl:otherwise>
      <xsl:if test="(@ID!=$OrderOne) and (@ID!=$OrderTwo) and (@ID!=$OrderThree) and (@ID!=$OrderFour)">
       <xsl:call-template name="SetCurrentNode">
        <xsl:with-param name="currentNode" select="."/>
       </xsl:call-template>
      </xsl:if>
      </xsl:otherwise>
    </xsl:choose>
   </xsl:for-each>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>



--  作者:cgp1996
--  发布时间:9/16/2004 7:38:00 PM

--  
太感谢了,今天晚上我试一下
--  作者:cgp1996
--  发布时间:9/16/2004 7:48:00 PM

--  
二楼太厉害了,结果完全一样,谢谢了!不过我现在还有点看不懂你的程序,以后请你多指教。
--  作者:doubleG
--  发布时间:9/17/2004 12:18:00 PM

--  
这个程序思路不复杂,只是涉及到递归调用,所以在模版之间传参数时看起来很乱。思路可能还是没有说清楚。具体是:
程序由四个模版组成:主模版,ReOrder,SetCurrentNode,SetChildrenNode.
ReOrder主要是根据四个不同的ID值来整理,所以在主模版里调用了四次ReOrder。在ReOrder中主要是遍历所有拥有当前处理的ID值的节点(<xsl:for-each select="//node()[@ID=$IDName]">),检查改节点的父亲节点是否含有当前处理的ID值或者是否是根节点,如果满足上面的那两个情况就略过该节点不进行处理;否则,调用SetCurrentNode设置该节点,在SetCurrentNode中设置完当前节点后检查这个节点的子节点,如果子节点集为空则略过否则编历子节点集,如果子节点的ID不为当前处理的ID值而是其余的三个则略过,否则就处理,如果继续处理的节点不为四个中的一个则直接设置,如果还是和当前处理ID值相同则设置完节点后调用SetChildrenNode在SetChildrenNode中又调用SetCurrentNode如此递归调用。
--  作者:cgp1996
--  发布时间:9/17/2004 11:33:00 PM

--  
doubleG你好,我分析一下你的程序,我觉得SetChildrenNode模板中有一段可以去掉:
       <xsl:call-template name="SetChildrenNode">
       <xsl:with-param name="IDName" select="$IDName"/>
       <xsl:with-param name="OrderName" select="$OrderName"/>
       <xsl:with-param name="currentNode" select="."/>
       </xsl:call-template>-->
我试了一下,结果是一样的,我不知道这一段是多余的还是另有用处,请指教。

--  作者:doubleG
--  发布时间:9/18/2004 3:24:00 PM

--  
哦,这是怕你的xml文件有更深层次啊,这个程序不仅限于你的那个xml文件,更多的层次也是可以的啊。呵呵
--  作者:cgp1996
--  发布时间:9/19/2004 12:10:00 AM

--  
好的,我再试一下
W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
113.281ms