V4N3X
V4N3X

Reputation: 1

Check value in an XML string with xsl

I'm new to the forum and even (little bit) new to xsl; I need help to solve a little problem with an XML code: please take a look at the structure below.

*********************************
<ProductRevision id="id45" **************>
  <ApplicationRef **************/>
  <UserData *********>
  <UserValue title="title1" value="11111 000 000"/>
  <UserValue title="object_name" value="test name1"/>
  </UserData>
  </ProductRevision>
<ProductRevision id="id50" ***********>
  <ApplicationRef **************/>
  <UserData id="id46">
  <UserValue title="title1" value="22222 000 000"/>
  <UserValue title="object_name" value="test name2"/>
  </UserData>
  </ProductRevision>
*******************************************
<GeneralRelation id="id49" subType="TestType" relatedRefs="#id2 #id45">
  <ApplicationRef **************/>
  <UserData ********>
  <UserValue title="ds5_amont" type="int" value="3"/>
  <UserValue title="ds5_cavities" type="int" value="2"/>
  </UserData>
  </GeneralRelation>
<GeneralRelation id="id49" subType="TestType" relatedRefs="#id2 #id50">
  <ApplicationRef ***********/>
  <UserData **********>
  <UserValue title="ds5_amont" type="int" value="2"/>
  <UserValue title="ds5_cavities" type="int" value="3"/>
  </UserData>
  </GeneralRelation>

As you can see, the ProductRevision nodes contain an id value; this value identifies two corresponding GeneralRelation nodes, which contain the UserValues ds5_amont and ds5_cavities. I use the following piece of .xsl code to display the values of title1 and object_name of all the ProductRevision nodes:

<xsl:for-each select="//plm:ProductRevision[@subType = 'XXXXX']">
  <xsl:variable name="part" select="./plm:UserData/plm:UserValue[@title='object_name']/@value" />
  <xsl:variable name="identnr" select="./plm:UserData/plm:UserValue[@title='title1']/@value" />
<Row>
  <Cell ss:StyleID="s73">
  <Data ss:Type="String">
  <xsl:value-of select="$part"/>
  </Data>
  </Cell>
  <Cell ss:StyleID="s73">
  <Data ss:Type="String">
  <xsl:value-of select="$identnr"/>
  </Data> 
  </Cell> 
</Row> 

Now, for each ProductRevision, I need to display the corresponding ds5_amont and ds5_cavities values, contained in the corresponding nodes identified by the id45 and id50 values. These properties must be printed next to the cells which display the $part and $identnr variables. I've not been able to find the solution so far, any help will be highly appreciated! Thanks!

EDIT

Sorry guys, I cannot learn in 2 seconds how to reproduce an Excel table on your forum. The result should be simply like that: for each Product revision, there should be one row showing object_name****title1****ds5_amont****ds5_cavities Sorry again, hope it is clear enough ;-)

Upvotes: 0

Views: 1199

Answers (3)

V4N3X
V4N3X

Reputation: 1

Thank you all for your contributions. Finally, this is the solution I've used. I guess yours were all very sophisticated and professional solutions, but for a beginner like me, it's necessary to simplify the code, in a way that even the errors are easily identifiable. But I can tell you that I will study a lot on those solutions! First of all, identify the correct ProductRevision nodes by the subType attribute; then, display the two variables in context of the ProductRevision nodes, and store the id in the pid variable:

    <xsl:template match="//plm:ProductRevision[@subType='DS5_PartRevision']">

    <xsl:variable name="part" select="./plm:UserData/plm:UserValue[@title='object_name']/@value" />
    <xsl:variable name="identnr" select="./plm:UserData/plm:UserValue[@title='ds5_s_serien_identnr']/@value" />
    <xsl:variable name="pid" select="@id"/>

    <Row>

        <Cell ss:StyleID="s73">
            <Data ss:Type="String">
                <xsl:value-of select="$part"/>
            </Data>
        </Cell>
        <Cell ss:StyleID="s73">
            <Data ss:Type="String">
                <xsl:value-of select="$identnr"/>
            </Data>  
        </Cell> 

Once the first ProductRevision node is processed, check all the GeneralRelation nodes; for each node, create the variable relref and store a portion of the value of the relatedRefs attribute. Only the value after " #" (space-#) will be stored:

<xsl:for-each select="/plm:PLMXML/plm:GeneralRelation">
            <xsl:variable name="relref" select="substring-after(@relatedRefs,' #')"/>

            <xsl:choose>
                <xsl:when test="$relref=$pid">
                    <Cell ss:StyleID="s73">
                        <Data ss:Type="String">
                            <xsl:value-of select="./plm:UserData/plm:UserValue[@title='ds5_amont']/@value"/>
                        </Data>  
                    </Cell>
                    <Cell ss:StyleID="s73">
                        <Data ss:Type="String">
                            <xsl:value-of select="./plm:UserData/plm:UserValue[@title='ds5_cavities']/@value"/>
                        </Data>  
                    </Cell>
                </xsl:when>
            </xsl:choose>
        </xsl:for-each>

    </Row>

For each GeneralRelation node, compare the values of pid and relref : if those are matching, get the values of the two properties in context of the GeneralRelation node (therefore the ./plm/ notation) and display them in the correct row (of the corresponding ProductRevision).

Upvotes: 0

Alejandro
Alejandro

Reputation: 1882

You want to process those GeneralRelation elements having the @id value of your current ProductRevision element in the list of theirs @relatedRefs attribute. In XSLT 1.0 you use contains and current functions like this:

/root
  /GeneralRelation
    [contains
       (concat(@relatedRefs,' '),
        concat('#',current()/@id,' ')
       )
    ]

This input:

<root>
    <ProductRevision id="id45">
        <ApplicationRef />
        <UserData>
            <UserValue title="title1" value="11111 000 000" />
            <UserValue title="object_name" value="test name1" />
        </UserData>
    </ProductRevision>
    <ProductRevision id="id50">
        <ApplicationRef />
        <UserData id="id46">
            <UserValue title="title1" value="22222 000 000" />
            <UserValue title="object_name" value="test name2" />
        </UserData>
    </ProductRevision>
    <GeneralRelation id="id49" subType="TestType"
        relatedRefs="#id2 #id45">
        <ApplicationRef />
        <UserData>
            <UserValue title="ds5_amont" type="int" value="453" />
            <UserValue title="ds5_cavities" type="int" value="452" />
        </UserData>
    </GeneralRelation>
    <GeneralRelation id="id49" subType="TestType"
        relatedRefs="#id2 #id50">
        <ApplicationRef />
        <UserData>
            <UserValue title="ds5_amont" type="int" value="502" />
            <UserValue title="ds5_cavities" type="int" value="503" />
        </UserData>
    </GeneralRelation>
</root>

And this transformation

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

    <xsl:variable name="vGeneralRelation" 
        select="/root/GeneralRelation" />

    <xsl:template match="text()|GeneralRelation" />
    <xsl:template match="root">
        <table>
            <xsl:apply-templates />
        </table>
    </xsl:template>

    <xsl:template match="ProductRevision">
        <Row>
            <xsl:apply-templates
                select="*|$vGeneralRelation[
                    contains(concat(@relatedRefs,' '),
                        concat('#',current()/@id,' '))]/*" />
        </Row>
    </xsl:template>

    <xsl:template
        match="UserValue[@title[.='object_name' or .='title1' or .='ds5_cavities' or .='ds5_amont']]">
        <Cell>
            <xsl:value-of select="@value" />
        </Cell>
    </xsl:template>
</xsl:stylesheet>

Produce this output

<?xml version="1.0" encoding="utf-8"?>
<table>
   <Row>
      <Cell>11111 000 000</Cell>
      <Cell>test name1</Cell>
      <Cell>453</Cell>
      <Cell>452</Cell>
   </Row>
   <Row>
      <Cell>22222 000 000</Cell>
      <Cell>test name2</Cell>
      <Cell>502</Cell>
      <Cell>503</Cell>
   </Row>
</table>

Check working example at http://xsltransform.net/6qaFCEU

Upvotes: 1

michael.hor257k
michael.hor257k

Reputation: 117140

Using the libxslt processor, you can take advantage of the EXSLT str:tokenize() extension function.

Consider the following simplified example:

XML

<root>
  <ProductRevision id="id45">
    <ApplicationRef/>
    <UserData>
      <UserValue title="title1" value="11111 000 000"/>
      <UserValue title="object_name" value="test name1"/>
    </UserData>
  </ProductRevision>
  <ProductRevision id="id50">
    <ApplicationRef/>
    <UserData id="id46">
      <UserValue title="title1" value="22222 000 000"/>
      <UserValue title="object_name" value="test name2"/>
    </UserData>
  </ProductRevision>
  <GeneralRelation id="id49" subType="TestType" relatedRefs="#id2 #id45">
    <ApplicationRef/>
    <UserData>
      <UserValue title="ds5_amont" type="int" value="453"/>
      <UserValue title="ds5_cavities" type="int" value="452"/>
    </UserData>
  </GeneralRelation>
  <GeneralRelation id="id49" subType="TestType" relatedRefs="#id2 #id50">
    <ApplicationRef/>
    <UserData>
      <UserValue title="ds5_amont" type="int" value="502"/>
      <UserValue title="ds5_cavities" type="int" value="503"/>
    </UserData>
  </GeneralRelation>
</root>

XSLT 1.0 + EXSLT

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

<xsl:key name="relation" match="GeneralRelation" use="str:tokenize(@relatedRefs, ' ')" />

<xsl:template match="/root">
    <table>
        <xsl:for-each select="ProductRevision">
            <Row>
                <!-- ProductRevision -->
                <Cell>
                    <xsl:value-of select="UserData/UserValue[@title='object_name']/@value"/>
                </Cell>
                <Cell>
                    <xsl:value-of select="UserData/UserValue[@title='title1']/@value"/>
                </Cell>
                <!-- GeneralRelation -->
                <xsl:variable name="relation" select="key('relation', concat('#', @id))" />
                <Cell>
                    <xsl:value-of select="$relation/UserData/UserValue[@title='ds5_amont']/@value"/>
                </Cell>
                <Cell>
                    <xsl:value-of select="$relation/UserData/UserValue[@title='ds5_cavities']/@value"/>
                </Cell>
            </Row> 
        </xsl:for-each>
    </table>
</xsl:template>

</xsl:stylesheet>

Result

<?xml version="1.0" encoding="UTF-8"?>
<table>
  <Row>
    <Cell>test name1</Cell>
    <Cell>11111 000 000</Cell>
    <Cell>453</Cell>
    <Cell>452</Cell>
  </Row>
  <Row>
    <Cell>test name2</Cell>
    <Cell>22222 000 000</Cell>
    <Cell>502</Cell>
    <Cell>503</Cell>
  </Row>
</table>

Upvotes: 1

Related Questions