0

I am having a hard time with the following use case.

Here is the XML:

<NodeA>
  <NodeB>
    <Application id="I-555" name="Text1" XorY="Y" />
    <Application id="I-666" name="Text2" XorY="X" />
    <Application id="I-777" name="Text3" XorY="Y" />
    <Application id="I-888" name="Text4" XorY="X" />
  </NodeB>
 </NodeA>

  <NodeD>
   <NodeE>
     <Process id="111" name="Text1" />
     <Process id="222" name="Text2" />
     <Process id="333" name="Text2" />
     <Process id="444" name="Text2" />
   </NodeE>
 </NodeD>

  <Links_between_Process_and_Application>
     <Link_Process_App app_id="I-555" process_id="111" />
     <Link_Process_App app_id="I-666" process_id="222" />
     <Link_Process_App app_id="I-777" process_id="333" />
     <Link_Process_App app_id="I-888" process_id="444" />
 </Links_between_Process_and_Application>

What I would like to achieve is to create/tranform a new XML with two new links, between the nodes "Application" and "Process", based on the node <Links_between_Process_and_Application>.

The tricky part is (at least for me) is to create two different links/nodes based on the attribute value from XorY (pseudocode):

While traversing (for-each) through <Links_between_Process_and_Application> IF(check for the current app_id (e.g. I-555) if @XorY='X' in the node "Application") create <Link_Process_Application> ELSE IF (check for the current app_id (e.g. I-555) if @XorY='Y' in the node "Application") create <Link_Process_IDP>

I have written two XSLTs for the transformation and generating these two links (without checking the attribute XorY), because I really don't know how to check the value of an attribute from an different for this use case:

<?xml version='1.0' ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:template match="/">
        <ImportSchemaBase>
            <xsl:for-each select="Links_between_Process_and_Application/Link_Process_App">                  
                    <Link_Process_Application>
                        <SourceKey>
                            <xsl:value-of select="@process_id"/>
                        </SourceKey>
                        <TargetKey>
                            <xsl:value-of select="@app_id"/>
                        </TargetKey>
                    </Link_Process_Application>
            </xsl:for-each>
        </ImportSchemaBase>
    </xsl:template>
</xsl:stylesheet>

and:

<?xml version='1.0' ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:template match="/">
       <ImportSchemaBase>
            <xsl:for-each select="Links_between_Process_and_Application/Link_Process_App">                  
                    <Link_Process_IDP>
                        <SourceKey>
                            <xsl:value-of select="@process_id"/>
                        </SourceKey>
                        <TargetKey>
                            <xsl:value-of select="@app_id"/>
                        </TargetKey>
                    </Link_Process_IDP>  
            </xsl:for-each>
        </ImportSchemaBase>
    </xsl:template>
</xsl:stylesheet>

The result should be:

Create Link_Process_IDP because for "app_id=I-555" XorY is Y

<Link_Process_IDP>
   <SourceKey>111</SourceKey>
   <TargetKey>I-555</TargetKey>
</Link_Process_IDP>

Create Link_Process_Application because for "app_id=I-666" XorY is X

<Link_Process_Application>
   <SourceKey>222</SourceKey>
    <TargetKey>I-666</TargetKey>
</Link_Process_Application>

Create Link_Process_IDP because for "app_id=I-777" XorY is Y

<Link_Process_IDP>
   <SourceKey>333</SourceKey>
    <TargetKey>I-777</TargetKey>
</Link_Process_IDP>

Create Link_Process_Application because for "app_id=I-888" XorY is X

<Link_Process_Application>
   <SourceKey>444</SourceKey>
    <TargetKey>I-888</TargetKey>
</Link_Process_Application>

Thank you a lot and sorry for the long description!

1
  • Where you have ELSEIF, you should also have ELSE. Otherwise the two states are mutually exclusive and there is no need to test for Y. Commented Nov 13, 2020 at 12:48

2 Answers 2

2

Your input is not XML - it lacks a single root element (and so does your expected output).

Given a well-formed XML input, the task is easy to achieve by using a key:

XML

<root> 
    <NodeA>
        <NodeB>
            <Application id="I-555" name="Text1" XorY="Y" />
            <Application id="I-666" name="Text2" XorY="X" />
            <Application id="I-777" name="Text3" XorY="Y" />
            <Application id="I-888" name="Text4" XorY="X" />
        </NodeB>
    </NodeA>
    <NodeD>
         <NodeE>
             <Process id="111" name="Text1" />
             <Process id="222" name="Text2" />
             <Process id="333" name="Text2" />
             <Process id="444" name="Text2" />
         </NodeE>
    </NodeD>
    <Links_between_Process_and_Application>
         <Link_Process_App app_id="I-555" process_id="111" />
         <Link_Process_App app_id="I-666" process_id="222" />
         <Link_Process_App app_id="I-777" process_id="333" />
         <Link_Process_App app_id="I-888" process_id="444" />
     </Links_between_Process_and_Application>
</root>

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:key name="app" match="Application" use="@id" />

<xsl:template match="/root">
    <output>
        <xsl:for-each select="Links_between_Process_and_Application/Link_Process_App">
            <xsl:variable name="xory" select="key('app', @app_id)/@XorY" />
            <xsl:variable name="name">
                <xsl:choose>
                    <xsl:when test="$xory = 'X'">Link_Process_Application</xsl:when>
                    <xsl:when test="$xory = 'Y'">Link_Process_IDP</xsl:when>
                    <xsl:otherwise>SomethingElse</xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <xsl:element name="{$name}">
                <SourceKey>
                    <xsl:value-of select="@process_id"/>
                </SourceKey>
                <TargetKey>
                    <xsl:value-of select="@app_id"/>
                </TargetKey>
            </xsl:element>
        </xsl:for-each>
    </output>
</xsl:template>

</xsl:stylesheet>

Result

<?xml version="1.0" encoding="UTF-8"?>
<output>
  <Link_Process_IDP>
    <SourceKey>111</SourceKey>
    <TargetKey>I-555</TargetKey>
  </Link_Process_IDP>
  <Link_Process_Application>
    <SourceKey>222</SourceKey>
    <TargetKey>I-666</TargetKey>
  </Link_Process_Application>
  <Link_Process_IDP>
    <SourceKey>333</SourceKey>
    <TargetKey>I-777</TargetKey>
  </Link_Process_IDP>
  <Link_Process_Application>
    <SourceKey>444</SourceKey>
    <TargetKey>I-888</TargetKey>
  </Link_Process_Application>
</output>

Note that if X and Y are mutually exclusive, you could shorten this further to:

<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:key name="app" match="Application" use="@id" />

<xsl:template match="/root">
    <output>
        <xsl:for-each select="Links_between_Process_and_Application/Link_Process_App">
            <xsl:variable name="name">
                <xsl:choose>
                    <xsl:when test="key('app', @app_id)/@XorY = 'X'">Link_Process_Application</xsl:when>
                    <xsl:otherwise>Link_Process_IDP</xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <xsl:element name="{$name}">
                <SourceKey>
                    <xsl:value-of select="@process_id"/>
                </SourceKey>
                <TargetKey>
                    <xsl:value-of select="@app_id"/>
                </TargetKey>
            </xsl:element>
        </xsl:for-each>
    </output>
</xsl:template>

</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

Comments

1

I would do it like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    
  <xsl:key name="app" match="Application" use="@id"/>

  <xsl:output method="xml" indent="yes"/>
  
  <xsl:template match="/">
      <xsl:apply-templates select="//Link_Process_App"/>
  </xsl:template>
  
  <xsl:template match="Link_Process_App[key('app', @app_id)/@XorY = 'X']">
                    <Link_Process_Application>
                        <SourceKey>
                            <xsl:value-of select="@process_id"/>
                        </SourceKey>
                        <TargetKey>
                            <xsl:value-of select="@app_id"/>
                        </TargetKey>
                    </Link_Process_Application>      
  </xsl:template>
  
  <xsl:template match="Link_Process_App[key('app', @app_id)/@XorY = 'Y']">
                                        <Link_Process_IDP>
                        <SourceKey>
                            <xsl:value-of select="@process_id"/>
                        </SourceKey>
                        <TargetKey>
                            <xsl:value-of select="@app_id"/>
                        </TargetKey>
                    </Link_Process_IDP>   
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bEJbVra/1

2 Comments

You defined <xsl:key name="process" match="Process" use="@id"/> but I don't see where you're using it.
@michael.hor257k, that's true, somehow I initially thought there was a need to cross-reference both type of elements and then I didn't notice I had the key in there without using it. The joys of small screens in a mobile world I guess.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.