XSL transform to replicate attributes from first p

2019-08-08 09:36发布

Hi This is an adaption of a previous question posted here.

XSLT - Find element values from first preceding item with matching name id - Using conditionals?

I have an XML that looks the Below.

I would like to create an xsl transform that produces the ideal xml at the bottom of the post.

Notice that within the XML presented, if a Image node is used more than once, defined by its file id , its subsequent children's elements are not present. For example the third image node, whose file id is the same as the first image node, does not contain the child properties and subsequent values. I want to add them back in.

I have the current sample xsl below that is basically working but I would like to add a conditional evaluation to the stylesheet that says: In English: if the 'key' element does not exist the find the first preceding matching 'file id' and use the values from those elements. The same would apply for the 'string and 'reel' elements. (any any others that pop up within the file node.

The result will fill the empty file nodes with all the relevant data based on the first instance of the matching file id.

Apologies for my poor description.

Any help will be greatly appreciated.

XML:

<?xml version="1.0"?>
<xmeml>
<boxset>
<stream>
    <track>
        <image>
            <start>0</start>
            <end>90</end>
            <file id="abcde">
                <key>95</key>
                <string>1023</string>
                <time>
                    <reel>142</reel>
                </time>
            </file>
        </image>
        <image>
            <start>90</start>
            <end>120</end>
            <file id="bcdef">
                <key>55</key>
                <string>1023</string>
                <time>
                    <reel>64</reel>
                </time>
            </file>
        </image>
    </track>
    <track>
        <image>
            <start>120</start>
            <end>130</end>
            <file id="abcde"/>
        </image>
        <image>
            <start>130</start>
            <end>180</end>
            <file id="cdefg">
                <key>92</key>
                <string>1023</string>
                <time>
                    <reel>194</reel>
                </time>
            </file>
        </image>    
    </track>
</stream>
</boxset>
</xmeml>

XSLT: Standard identity transform

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>


<!-- identity Pass everything  -->
<xsl:template match="@*|node()">
<xsl:copy>
  <xsl:apply-templates select="@*|node()"/>
</xsl:copy> 
</xsl:template>

</xsl:stylesheet>

If the XML were to be filled they way Id like to see it it would look like this.

Ideal XML:

<?xml version="1.0"?>
<xmeml>
<boxset>
<stream>
    <track>
        <image>
            <start>0</start>
            <end>90</end>
            <file id="abcde">
                <key>95</key>
                <string>1023</string>
                <time>
                    <reel>142</reel>
                </time>
            </file>
        </image>
        <image>
            <start>90</start>
            <end>120</end>
            <file id="bcdef">
                <key>55</key>
                <string>1023</string>
                <time>
                    <reel>64</reel>
                </time>
            </file>
        </image>
    </track>
    <track>
        <image>
            <start>120</start>
            <end>130</end>
<!-- ideal data for repeated file "abcde" -->
            <file id="abcde">
                <key>95</key>
                <string>1023</string>
                <time>
                    <reel>142</reel>
                </time>
            </file>
<!-- end ideal repeated data -->
        </image>
        <image>
            <start>130</start>
            <end>180</end>
            <file id="cdefg">
                <key>92</key>
                <string>1023</string>
                <time>
                    <reel>194</reel>
                </time>
            </file>
        </image>    
    </track>
</stream>
</boxset>
</xmeml>

标签: xml xslt
2条回答
太酷不给撩
2楼-- · 2019-08-08 10:21

This transformation:

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

 <xsl:key name="kImageById" match="image" use="file/@id"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match=
  "image[not(generate-id()
            =generate-id(key('kImageById',file/@id)[1])
            )]">
  <xsl:copy>
   <xsl:apply-templates select="@*|start|end"/>
   <xsl:apply-templates select=
   "key('kImageById',file/@id)[1]/*[not(self::start or self::end)]"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<xmeml>
    <boxset>
        <stream>
            <track>
                <image>
                    <start>0</start>
                    <end>90</end>
                    <file id="abcde">
                        <key>95</key>
                        <string>1023</string>
                        <time>
                            <reel>142</reel>
                        </time>
                    </file>
                </image>
                <image>
                    <start>90</start>
                    <end>120</end>
                    <file id="bcdef">
                        <key>55</key>
                        <string>1023</string>
                        <time>
                            <reel>64</reel>
                        </time>
                    </file>
                </image>
            </track>
            <track>
                <image>
                    <start>120</start>
                    <end>130</end>
                    <file id="abcde"/>
                </image>
                <image>
                    <start>130</start>
                    <end>180</end>
                    <file id="cdefg">
                        <key>92</key>
                        <string>1023</string>
                        <time>
                            <reel>194</reel>
                        </time>
                    </file>
                </image>
            </track>
        </stream>
    </boxset>
</xmeml>

produces the wanted, correct result:

<xmeml>
   <boxset>
      <stream>
         <track>
            <image>
               <start>0</start>
               <end>90</end>
               <file id="abcde">
                  <key>95</key>
                  <string>1023</string>
                  <time>
                     <reel>142</reel>
                  </time>
               </file>
            </image>
            <image>
               <start>90</start>
               <end>120</end>
               <file id="bcdef">
                  <key>55</key>
                  <string>1023</string>
                  <time>
                     <reel>64</reel>
                  </time>
               </file>
            </image>
         </track>
         <track>
            <image>
               <start>120</start>
               <end>130</end>
               <file id="abcde">
                  <key>95</key>
                  <string>1023</string>
                  <time>
                     <reel>142</reel>
                  </time>
               </file>
            </image>
            <image>
               <start>130</start>
               <end>180</end>
               <file id="cdefg">
                  <key>92</key>
                  <string>1023</string>
                  <time>
                     <reel>194</reel>
                  </time>
               </file>
            </image>
         </track>
      </stream>
   </boxset>
</xmeml>
查看更多
闹够了就滚
3楼-- · 2019-08-08 10:29

I think it would be simpler to just handle the file without bothering about the image:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:key name="file" match="file[*]" use="@id"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="file[not(*)]">
    <xsl:copy-of select="key('file', @id)" />
  </xsl:template>

</xsl:stylesheet>
查看更多
登录 后发表回答