Home > Software design >  Adding element if attribute value does not exist
Adding element if attribute value does not exist

Time:12-19

I am working with a file which looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<items>
<item sort="gruun">
   <entrycl>
      <entry>gruun</entry>
      <gramt>bn</gramt></entrycl>
<translcl>
   <transl>green</transl>
</translcl>
</item>

<item>
   <entrycl placeatentry="gruun">
   <entryx>it is grün</entryx>
</entrycl>
   <translcl><transl>bla bla bla</transl>
</translcl>
</item>

<item>
   <entrycl placeatentry="blue">
   <entryx>it is blue</entryx></entrycl>
   <translcl>
      <transl>blu blu blu</transl>
</translcl>
</item>
</items>

I want to add an entrycl/entry with the value of @placeatentry, but only if there is no other item with this value. In order to facilitate checking the result I want to put an attribute (new ="yes").

The expected outcome is this:

<items>
<item sort="gruun">
   <entrycl>
      <entry>gruun</entry>
      <gramt>bn</gramt></entrycl>
<translcl>
   <transl>ble ble ble</transl>
<rem>existing item</rem>
</translcl>
</item>

<item>
   <entrycl placeatentry="gruun">
   <entryx>it is grün</entryx>
</entrycl>
   <translcl><transl>bla bla bla</transl>
</translcl>
</item>

<item>
   <entrycl new="yes">
      <entry>blue</entry>
   <entryx>it is blue</entryx></entrycl>
   <translcl>
      <transl>blu blu blu</transl>
</translcl>
</item>
</items>

Only the blue should a new entrycl/entry because there is no existing entrycl/entry with the value 'blue'.

The next step is to connect the items which did not get a new entrycl to the existing item, but I think how to do that.

I tried numerous times, benefitting from answers to other questions. Now I have this xslt file:

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

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

<xsl:template match="item[entrycl/@placeatentry and not(//entry[.=entrycl/@placeatentry])]">
    <xsl:copy>
      <entrycl>
        <entry new="yes">
          <xsl:value-of select="entrycl/@placeatentry"/>
        </entry>
        <xsl:apply-templates select="entrycl/node()"/>
      </entrycl>
      <xsl:apply-templates select="@*|node()[not(self::entrycl)]"/>
    </xsl:copy>
  <!-- </xsl:if> -->
  </xsl:template>
</xsl:stylesheet>

Yet my stylesheet places a new entry to all items:

<?xml version="1.0" encoding="UTF-8"?>
<items>
   <item sort="gruun">
      <entrycl>
         <entry>gruun</entry>
         <gramt>bn</gramt>
      </entrycl>
      <translcl>
         <transl>green</transl>
      </translcl>
   </item>
   <item>
      <entrycl>
         <entry new="yes">gruun</entry>
         <entryx>it is grün</entryx>
      </entrycl>
      <translcl>
         <transl>bla bla bla</transl>
      </translcl>
   </item>
   <item>
      <entrycl>
         <entry new="yes">blue</entry>
         <entryx>it is blue</entryx>
      </entrycl>
      <translcl>
         <transl>blu blu blu</transl>
      </translcl>
   </item>
</items>

What am I doing wrong? Am I overlooking something? Thank you in advance for your help.

CodePudding user response:

See if this simplified example can get you on the right track:

XML

<items>
    <item>
        <entrycl>
            <entry>gruun</entry>
        </entrycl>
    </item>
    <item>
        <entrycl placeatentry="gruun">
        </entrycl>
    </item>
    <item>
        <entrycl placeatentry="blue">
        </entrycl>
    </item>
</items>

XSLT 1.0

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

<xsl:key name="k1" match="entrycl" use="entry" />

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

<xsl:template match="entrycl[@placeatentry][not(key('k1', @placeatentry))]">
    <xsl:copy>
        <xsl:attribute name="new">yes</xsl:attribute>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Result

<?xml version="1.0" encoding="UTF-8"?>
<items>
   <item>
      <entrycl>
         <entry>gruun</entry>
      </entrycl>
   </item>
   <item>
      <entrycl placeatentry="gruun"/>
   </item>
   <item>
      <entrycl new="yes" placeatentry="blue"/>
   </item>
</items>

Note that the search for a matching value in entry includes the current entrycl.

  • Related