1

要素が重複している XML 内の場所を見つける必要があります。たとえば、XML では次のようになります。

<menu>
  <juice sugar="yes" fresh="no">
    <apple/>
    <carrot/>
  </juice>
  <juice sugar="no" fresh="no">
    <apple/>
    <carrot/>
  </juice>
  <juice sugar="no" fresh="no">
    <carrot/>
    <apple/>
  </juice>
  <juice>
    <carrot kind="village" />
    <orange/>
  <juice/>
  <juice>
    <carrot kind="village" />
    <orange/>
    <carrot kind="village" />
  </juice>
</menu>

XML に共通の親を持つ等しい要素が含まれている場合、例外をスローする必要があります。ただし、属性も重要であり、等しい必要があります。

子孫の順序は重要ではありません。つまり、私の例では、例外がスローされる必要があります

  <juice sugar="no" fresh="no">
    <apple/>
    <carrot/>
  </juice>
  <juice sugar="no" fresh="no">
    <carrot/>
    <apple/>
  </juice>

「ジュース」はすべての属性が等しく、2 番目の「ジュース」の子は最初の子の置換子であるためです。

例外が発生する別の場所は次のとおりです。

  <juice>
    <carrot kind="village" />
    <orange/>
    <carrot kind="village" />
  </juice>

「ジュース」には同じ「ニンジン」が 2 回含まれているためです。

この問題のヒントに感謝します。XSLT を使用する必要がありますか? それとも、C# で XML を逆シリアル化する方がよいのでしょうか?

4

3 に答える 3

2

まず、子を順番に並べ替えて、各要素を標準形式に変換します。これは、XSLT を使用して簡単に実行できます。結果は、XPath 2.0 に従って標準的な形式が deep-equal() である場合にのみ、ルールに従って 2 つの要素が等しいものになるはずです。

次に、各要素に対してある種のハッシュコードを計算する関数を作成し ("等しい" 要素が等しいハッシュコードを持つようにする)、このハッシュコードでグループ化を実行します。これも、XSLT 2.0 で簡単に実行できます。唯一難しいのは、ハッシュ関数の設計です。あなたの例には実際のデータが表示されていないと思われます。ハッシュ関数を提案する前に、実際のデータを確認したいと思います。

次に、各ハッシュコード グループ内で、XSLT 2.0 の deep-equal() 関数を使用して、グループのすべてのメンバーを他のすべてのメンバーと比較し、ハッシュコードの一致が誤っているケースを排除できます。

于 2013-08-04T23:18:40.490 に答える
1

以下の XSLT 2.0 ソリューションは、たまたまデータ セットで動作します。実行するデータがさらにある場合は、それがどれほど堅牢であるかをテストするのに役立ちます.

t:\ftemp>type viktoria.xml 
<?xml version="1.0" encoding="UTF-8"?>
<menu>
  <juice sugar="yes" fresh="no">
    <apple/>
    <carrot/>
  </juice>
  <juice sugar="no" fresh="no">
    <apple/>
    <carrot/>
  </juice>
  <juice sugar="no" fresh="no">
    <carrot/>
    <apple/>
  </juice>
  <juice>
    <carrot kind="village" />
    <orange/>
  </juice>
  <juice>
    <carrot kind="village" />
    <orange/>
    <carrot kind="village" />
  </juice>
</menu>

t:\ftemp>call xslt2 viktoria.xml viktoria.xsl 
<?xml version="1.0" encoding="UTF-8"?>
<exceptions>
   <duplicates>
      <juice sugar="no" fresh="no">
         <apple/>
         <carrot/>
      </juice>
      <juice sugar="no" fresh="no">
         <carrot/>
         <apple/>
      </juice>
   </duplicates>
   <children>
      <juice>
         <carrot kind="village"/>
         <orange/>
         <carrot kind="village"/>
      </juice>
   </children>
</exceptions>

t:\ftemp>type viktoria.xsl 
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:v="urn:X-Viktoria" exclude-result-prefixes="v xsd"
                version="2.0">

<xsl:output indent="yes"/>

<!--return true if the two elements and their attributes are the same while
    ignoring children-->
<xsl:function name="v:shallow-equal" as="xsd:boolean">
  <xsl:param name="elem1" as="element()"/>
  <xsl:param name="elem2" as="element()"/>
  <xsl:sequence select="node-name($elem1)=node-name($elem2) and
    ( every $a1 in $elem1/@* satisfies ( some $a2 in $elem2/@* satisfies 
        ( node-name($a1)=node-name($a2)  and $a1 = $a2 ) ) ) and
    ( every $a2 in $elem2/@* satisfies ( some $a1 in $elem1/@* satisfies 
        ( node-name($a1)=node-name($a2)  and $a1 = $a2 ) ) )"/>
</xsl:function>

<!--return true if two elements have the same children with the same attributes
    while ignoring the children's children-->
<xsl:function name="v:element-and-children-equal" as="xsd:boolean">
  <xsl:param name="elem1" as="element()"/>
  <xsl:param name="elem2" as="element()"/>
  <xsl:sequence
    select="v:shallow-equal($elem1,$elem2) and
            ( every $child1 in $elem1/* satisfies 
                count( $elem2/*[deep-equal(.,$child1)] )=1 ) and
            ( every $child2 in $elem2/* satisfies 
                count( $elem1/*[deep-equal(.,$child2)] )=1 )"/>
</xsl:function>

<!--produce result-->
<xsl:template match="menu">
  <exceptions>
    <duplicates>
      <!--find each element that has a sibling with same children, that is,
          there is more than one such element amongst all siblings-->
      <xsl:for-each 
        select="*[ for $this in . return
              count ( ../*[v:element-and-children-equal(.,$this)] ) > 1 ]">
       <xsl:copy-of select="."/>
      </xsl:for-each>
    </duplicates>
    <children>
      <!--find each element that has duplicate children, that is,
          there is more than one of each child amongst all children-->
      <xsl:for-each
        select="*[ some $child in * satisfies
                   count ( *[deep-equal(.,$child)] ) >1 ]">
       <xsl:copy-of select="."/>
      </xsl:for-each>
    </children>
  </exceptions>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>rem Done! 
于 2013-08-05T00:31:16.960 に答える
0

ご回答ありがとうございます。C# と XmlDocument クラスで問題を解決しました。幸いなことに、特定の属性を持つノードのみをチェックするだけで十分であり、デシリアライズする必要がないことを発見しました。各ノードの子孫を再帰的にチェックしました。

于 2013-08-19T18:39:36.237 に答える