在XML文档中的其他可选元素之间插入可选元素...根据架构?

时间:2014-01-17 15:56:59

标签: c# xml xslt infopath

非常难以将解释纳入准确的标题,但我尝试过。

无论如何这个问题很简单。

给出以下xml:

<Parent> 
  <Child/> 
  <Child/> 
  <Child/> 
  <Child/> 
  <!-- Trying to insert an element here -->
  <OtherChild/>
  <OtherChild/>
  <OtherChild/>
</Parent>

并假设如下:

  • 我们知道架构并可以访问它。
  • 根据架构,所有元素都是可选的,但必须按顺序显示。
  • 我们插入的元素也可以是可选的。
  • 无法保证任何父母或其他子节点在那里或将有多少。
  • 嵌套深度不一定是1,它可能是多个嵌套元素。

我的解决方案:

  1. 检查我们是否插入了任何一个孩子。
  2. 检查以前兄弟姐妹的递归方法(需要知道我们插入的元素之前的兄弟姐妹),然后检查父母。
  3. 结果:

    一个。如果我们试图插入的元素存在,我们插入后。
    B.如果存在先前的兄弟姐妹,我们之后插入 C.如果不存在先前的兄弟姐妹,但是父母我们是否在父母之前 D.从那里向上走树,我们将继续前往当前父节点的下一个父节点或前一节节点,直到我们找到一个存在的节点,然后将整个树插入到我们试图插入的元素中。

    我目前正在C#代码中执行此操作,首先使用模式发现前面的兄弟,然后迭代地尝试选择一个,直到我找到一个或到达列表的末尾。然后我尝试发现父母然后重复父母作为要插入的元素。

    问题:

    是否有可能以另一种更快,更有效,更整洁,更好的方式做到这一点?

    我刚学习XSLT,是否可以使用完美的转换(注意必须是XSLT 1.0)。

    另外,聪明的方法可以最大限度地减少目前的工作,它基本上必须遍历整个树,直到它到达一个可能需要大量工作的现有节点。

    注意:作为参考,这实际上是在InfoPath中使用,我将在其中处理主数据源。特别是我指的是重复表,用户可以从表中删除所有行,此时任何试图向该表添加行的代码都可能非常困难。

    InfoPath本身能够像饼一样简单地向表中添加新行,如果我能弄清楚它们如何管理它会很棒。取而代之的是,我很高兴只是有一个简单的方法来做到这一点。

    我已经找到了问题的解决方案,正如我所说,我只是在寻找一些人来反省意见,看看我是否能得到一些改进。我感谢您提出的任何建议。

    谢谢:)

2 个答案:

答案 0 :(得分:0)

这不是一个完整的答案,但如果你给我一些澄清,我可能会解决一些问题。

以下给出了xml:

<root>
    <parent>
        <child>child1</child>
        <child>child2</child>
        <otherChild>other1</otherChild>
    </parent>
</root>

将其转换为:

<?xml version="1.0" encoding="UTF-8"?> 
<root>
    <parent>
        <child>child1</child>    
        <child>child2</child>
        <insertedChild>inserted1</insertedChild>
        <otherChild>other2</otherChild>
    </parent>
</root>

这是xsl:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="1.0"> 
  <xsl:template match="/root"> 
    <xsl:for-each select=".">
      <xsl:element name="root">
          <xsl:for-each select="parent">
              <xsl:element name="parent">
                  <xsl:copy-of select="child" />
                  <xsl:element name="insertedChild">
                    inserted1
                  </xsl:element>
                 <xsl:copy-of select="otherChild" />            
              </xsl:element>
           </xsl:for-each>      
      </xsl:element>
    </xsl:for-each>
  </xsl:template> 
</xsl:stylesheet>

对于我们希望添加的元素之前和之后的任意数量的不同元素,可以对其进行更改。

我从问题描述中得不到的是要添加的元素是否可以在任何地方;也就是说,我们事先并不知道我们希望结束的确切模式。

这是一个需要为多个元素运行的转换吗?如果是这种情况,您可能需要创建多个xsl文件或将其包含在此文件中。无论哪种方式,它都会有点复杂。

然而,

XSL对参数化是开放的,因为您可以选择插入元素的名称和值,例如:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="1.0"> 
<xsl:param name="insertedChild"></xsl:param>
<xsl:param name="valueOfInsertedChild"></xsl:param>
  <xsl:template match="/root"> 
    <xsl:for-each select=".">
        <xsl:element name="root">
            <xsl:for-each select="parent">
                <xsl:element name="parent">
                    <xsl:copy-of select="child" />
                    <xsl:element name="{$insertedChild}">
                        <xsl:value-of select="$valueOfInsertedChild" />
                    </xsl:element>
                    <xsl:copy-of select="otherChild" />         
                </xsl:element>
            </xsl:for-each>     
        </xsl:element>
    </xsl:for-each>
  </xsl:template> 
</xsl:stylesheet>

我希望这甚至有点接近你想要的东西。澄清上面提到的要点,我们可能会做得更好。

答案 1 :(得分:0)

好的,我相信我得到了这个。声明是4个参数,定义插入元素的名称,值,一个参数定义哪个是前一个元素,一个定义父元素。

两个限制:

  • 需要知道根(文档)元素(这里 “根”)。
  • 您无法获得混合内容(即插入节点) 另一个有字符数据而不是其他节点。)

    <?xml version="1.0"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="1.0"> 
    <xsl:param name="insertedChild">a</xsl:param>
    <xsl:param name="valueOfInsertedChild">a</xsl:param>
    <xsl:param name="appendAfterChild">child</xsl:param>
    <xsl:param name="appendIntoNode">parent</xsl:param>
    
     <xsl:template match="node()|@*">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*" />
         </xsl:copy>
     </xsl:template>
    
    <xsl:template match="root">
        <xsl:choose>
        <xsl:when test="name() = $appendIntoNode">    
            <xsl:call-template name="insertNode" />
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="recurse" />
        </xsl:otherwise>
        </xsl:choose>
     </xsl:template>
    
    <xsl:template name="recurse">
        <xsl:copy>
        <xsl:for-each select="child::node()">
        <xsl:choose>
        <xsl:when test="name() = $appendIntoNode">    
            <xsl:call-template name="insertNode" />
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="recurse" />
        </xsl:otherwise>
        </xsl:choose>
        </xsl:for-each>
        </xsl:copy>
     </xsl:template>
    
      <xsl:template name="insertNode">    
            <xsl:element name="{name()}">
            <xsl:for-each select="*[(following-sibling::*[name()=$appendAfterChild] and name()!=$appendAfterChild) or name()=$appendAfterChild]">
                    <xsl:copy-of select="." />
            </xsl:for-each>
            <xsl:element name="{$insertedChild}">
                    <xsl:value-of select="$valueOfInsertedChild" />
            </xsl:element>
            <xsl:for-each select="*[not((following-sibling::*[name()=$appendAfterChild] and name()!=$appendAfterChild) or name()=$appendAfterChild)]">
                    <xsl:copy-of select="." />
            </xsl:for-each>
            </xsl:element> 
     </xsl:template>
    
    </xsl:stylesheet>
    

由于性能原因,我不确定它是如何工作的,但你可以尝试一下。我认为这与你的要求非常接近。让我知道这是怎么回事。 :)