Search code examples
xmlxsltgraphml

XML document transforms using XSLT to GraphML in a single line


I've compiled an XSL document to transform custom XML documents to GraphML (with some additional metadata for the yEd Graph Editor). The transformation works as expected and completes, but there's one problem. No matter which tool I've used to perform the transformation, the resulting document is comprised of a single line, so I have to format it every time I perform a transformation. My question is:

How can I adjust my XSL document to have a pretty-printed XML document?

OR

What am I doing wrong?

My XSL code is the following:

<xsl:stylesheet version="1.0" encoding="UTF-8" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/paths">
        <graphml xmlns="http://graphml.graphdrawing.org/xmlns"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:y="http://www.yworks.com/xml/graphml"
 xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd">
            <key id="workerDescription" for="node" yfiles.type="nodegraphics"/>
            <key for="edge" id="response" yfiles.type="edgegraphics"/>
            <xsl:for-each select="path">
                <graph edgedefault="directed">
                    <xsl:attribute name="id">
                        <xsl:value-of select="@id"/>
                    </xsl:attribute>

                    <node>
                        <xsl:attribute name="id">
                            <xsl:value-of select="@id"/>
                        </xsl:attribute>
                        <data key="workerDescription">
                            <y:ShapeNode>
                                <y:Fill color="#FFCC00" transparent="false"/>
                                <y:NodeLabel>
                                    <xsl:value-of select="@id"/>
                                </y:NodeLabel>
                            </y:ShapeNode>
                        </data>
                    </node>

                    <xsl:for-each select="entry-point">
                        <edge>
                            <xsl:attribute name="source">
                                <xsl:value-of select="../@id"></xsl:value-of>
                            </xsl:attribute>
                            <xsl:attribute name="target">
                                <xsl:value-of select="../@id"/>.<xsl:value-of select="@worker-ref"/>
                            </xsl:attribute>
                        </edge>
                    </xsl:for-each>

                    <xsl:for-each select="worker">
                        <node>
                            <xsl:attribute name="id">
                                <xsl:value-of select="../@id"/>.<xsl:value-of select="@id"/>
                            </xsl:attribute>
                            <data key="workerDescription">
                                <y:ShapeNode>
                                    <xsl:attribute name="alignment">center</xsl:attribute>
                                    <xsl:attribute name="autoSizePolicy">content</xsl:attribute>
                                    <xsl:attribute name="modelName">internal</xsl:attribute>
                                    <xsl:attribute name="modelPosition">c</xsl:attribute>
                                    <y:NodeLabel>
                                        <xsl:choose>
                                            <xsl:when test="@idref = 'CallPath'">
                                                <xsl:value-of select="@id"/> &gt; <xsl:value-of select="context/value/map/entry/@value"/>
                                            </xsl:when>
                                            <xsl:otherwise>
                                                <xsl:value-of select="@id"/> &gt; <xsl:value-of select="@idref"/>
                                            </xsl:otherwise>
                                        </xsl:choose>
                                    </y:NodeLabel>
                                    <xsl:if test="@idref = 'CallPath'">
                                        <y:Fill color="#FFCC00" transparent="false"/>
                                    </xsl:if>
                                </y:ShapeNode>
                            </data>
                        </node>

                        <xsl:for-each select="response">
                            <edge>
                                <xsl:attribute name="source">
                                    <xsl:value-of select="../../@id"/>.<xsl:value-of select="../@id"/>
                                </xsl:attribute>
                                <xsl:attribute name="target">
                                    <xsl:value-of select="../../@id"/>.<xsl:value-of select="@worker-ref | @exit"/>
                                </xsl:attribute>
                                <xsl:attribute name="directed">true</xsl:attribute>
                                <data key="response">
                                    <y:PolyLineEdge>
                                        <y:EdgeLabel>
                                            <xsl:value-of select="@name"/>
                                        </y:EdgeLabel>
                                        <y:Arrows source="none" target="standard"/>
                                    </y:PolyLineEdge>
                                </data>
                            </edge>

                            <xsl:for-each select="@exit">
                                <node>
                                    <xsl:attribute name="id">
                                        <xsl:value-of select="../../../@id"/>.<xsl:value-of select="."/>
                                    </xsl:attribute>
                                    <data key="workerDescription" transparent="false">
                                        <y:ShapeNode>
                                            <xsl:attribute name="alignment">center</xsl:attribute>
                                            <xsl:attribute name="autoSizePolicy">content</xsl:attribute>
                                            <xsl:attribute name="modelName">internal</xsl:attribute>
                                            <xsl:attribute name="modelPosition">c</xsl:attribute>
                                            <y:Fill color="#FF0000"/>
                                            <y:NodeLabel>
                                                <xsl:value-of select="."/>
                                            </y:NodeLabel>
                                        </y:ShapeNode>
                                    </data>
                                </node>
                            </xsl:for-each>
                        </xsl:for-each>
                    </xsl:for-each>
                </graph>
            </xsl:for-each>
        </graphml>
    </xsl:template>
</xsl:stylesheet>

My input is the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE paths SYSTEM "paths.dtd">
<paths>
    <path id="PathID">
        <entry-point name="start" worker-ref="EntryWorker" />
        <worker id="EntryWorker" idref="EntryWorkerClass">
            <response name="SUCCESS" worker-ref="MarkScoring" />
            <response name="ERROR" exit="ERRORM" />
        </worker>
        <worker id="MarkScoring" idref="MarkScoringClass">
            <response name="SUCCESS" exit="SUCCESS" />
            <response name="ERROR" exit="ERRORSCOR" />
        </worker>
    </path>
</paths>

And my output is the following:

<?xml version="1.0"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd">
    <key id="workerDescription" for="node" yfiles.type="nodegraphics"/>
    <key for="edge" id="response" yfiles.type="edgegraphics"/>
    <graph edgedefault="directed" id="PathID">
        <node id="PathID">
            <data key="workerDescription">
                <y:ShapeNode>
                    <y:Fill color="#FFCC00" transparent="false"/>
                    <y:NodeLabel>PathID</y:NodeLabel>
                </y:ShapeNode>
            </data>
        </node>
        <edge source="PathID" target="PathID.EntryWorker"/>
        <node id="PathID.EntryWorker">
            <data key="workerDescription">
                <y:ShapeNode alignment="center" autoSizePolicy="content" modelName="internal" modelPosition="c">
                    <y:NodeLabel>EntryWorker &gt; EntryWorkerClass</y:NodeLabel>
                </y:ShapeNode>
            </data>
        </node>
        <edge source="PathID.EntryWorker" target="PathID.MarkScoring" directed="true">
            <data key="response">
                <y:PolyLineEdge>
                    <y:EdgeLabel>SUCCESS</y:EdgeLabel>
                    <y:Arrows source="none" target="standard"/>
                </y:PolyLineEdge>
            </data>
        </edge>
        <edge source="PathID.EntryWorker" target="PathID.ERRORM" directed="true">
            <data key="response">
                <y:PolyLineEdge>
                    <y:EdgeLabel>ERROR</y:EdgeLabel>
                    <y:Arrows source="none" target="standard"/>
                </y:PolyLineEdge>
            </data>
        </edge>
        <node id="PathID.ERRORM">
            <data key="workerDescription" transparent="false">
                <y:ShapeNode alignment="center" autoSizePolicy="content" modelName="internal" modelPosition="c">
                    <y:Fill color="#FF0000"/>
                    <y:NodeLabel>ERRORM</y:NodeLabel>
                </y:ShapeNode>
            </data>
        </node>
        <node id="PathID.MarkScoring">
            <data key="workerDescription">
                <y:ShapeNode alignment="center" autoSizePolicy="content" modelName="internal" modelPosition="c">
                    <y:NodeLabel>MarkScoring &gt; MarkScoringClass</y:NodeLabel>
                </y:ShapeNode>
            </data>
        </node>
        <edge source="PathID.MarkScoring" target="PathID.SUCCESS" directed="true">
            <data key="response">
                <y:PolyLineEdge>
                    <y:EdgeLabel>SUCCESS</y:EdgeLabel>
                    <y:Arrows source="none" target="standard"/>
                </y:PolyLineEdge>
            </data>
        </edge>
        <node id="PathID.SUCCESS">
            <data key="workerDescription" transparent="false">
                <y:ShapeNode alignment="center" autoSizePolicy="content" modelName="internal" modelPosition="c">
                    <y:Fill color="#FF0000"/>
                    <y:NodeLabel>SUCCESS</y:NodeLabel>
                </y:ShapeNode>
            </data>
        </node>
        <edge source="PathID.MarkScoring" target="PathID.ERRORSCOR" directed="true">
            <data key="response">
                <y:PolyLineEdge>
                    <y:EdgeLabel>ERROR</y:EdgeLabel>
                    <y:Arrows source="none" target="standard"/>
                </y:PolyLineEdge>
            </data>
        </edge>
        <node id="PathID.ERRORSCOR">
            <data key="workerDescription" transparent="false">
                <y:ShapeNode alignment="center" autoSizePolicy="content" modelName="internal" modelPosition="c">
                    <y:Fill color="#FF0000"/>
                    <y:NodeLabel>ERRORSCOR</y:NodeLabel>
                </y:ShapeNode>
            </data>
        </node>
    </graph>
</graphml>

I'm using Notepad++ "XML Tools" plugin to transform and format my documents. Any suggestion to incorporate an XSLT transformation with formatted end results to my workflow (programmatically or otherwise) is welcome.


Solution

  • Change this part:

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

    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"/>