Creating a structural chart using XML and rendered via LaTeX

174 Views Asked by At

I am trying to recreate a structural chart through XSLT with already predefined formatting i.e, their appearance defined separately. The structure is written using XML but later transformed into LaTeX code using forest package in directory tree style with the help of xml2tex, an Xproc library (https://github.com/transpect/xml2tex). Since they are not the main part of discussion so I am not elaborating them further.

What I do have is a sample structure in the form:

<structure>
  <structentry id="1" parent="0" caller="true">Root</structentry>
  <structentry id="2" parent="1" caller="false">Child 1 of ID 1 (Root)</structentry>
  <structentry id="3" parent="1" caller="false">Child 2 of ID 1 (Root)</structentry>
  <structentry id="4" parent="1" caller="false">Child 3 of ID 1 (Root)</structentry>
  <structentry id="5" parent="4" caller="false">Child 1 of ID 4</structentry>
  <structentry id="6" parent="5" caller="false">Child 1 of ID 5</structentry>
</structure>

Ideally the output should be in the form:

[{Root},fill=white[{Child 1 of ID 1 (Root)}][{Child 2 of ID 1 (Root)}][{Child 3 of ID 1 (Root)}[{Child 1 of ID 4}[{Child 1 of ID 5}]]]]

Or for readability' sake:

[{Root},fill=white
  [{Child 1 of ID 1 (Root)}]
  [{Child 2 of ID 1 (Root)}]
  [{Child 3 of ID 1 (Root)}
    [{Child 1 of ID 4}
      [{Child 1 of ID 5}]
    ]
  ]
]

which then visually looks like this:

Sample structure

Hence, I am trying to put nodes under their parents through their matching ids. That is, any node having parent='1' should be child of node having id='1'.

I have the following transformation which uses 'dbk' (DocBook) namespaces for defining nodes in the input XML. Since I am very new in XML, XSLT and XPath, I can't help but remain stuck here after xsl:template match="dbk:structentry". If any additional information is required I will gladly update them.

  <template context="dbk:structure">
    <text>\begin{center}&#xa;</text>
    <xsl:apply-templates select="dbk:caption"/>
    <text>\rule[5pt]{0.8\textwidth}{0.4pt}&#xa;</text>
    <text>&#xa;\begin{forest}</text>
    <xsl:apply-templates select="dbk:structentry"/>
    <text>\end{forest}</text>
    <text>&#xa;\end{center}</text>
  </template>

  <xsl:template match="dbk:caption">
    \textbf{<xsl:value-of select="."/>}&#xa;
  </xsl:template>
  
  <xsl:template match="dbk:structentry">
    <xsl:choose>
    <xsl:when test="@parent eq '0' and @caller eq 'true'">
      [{<xsl:value-of select="."/>},fill=white<xsl:apply-templates select="@parent eq '1'">]
    </xsl:when>
    <xsl:otherwise>
      [{<xsl:value-of select="."/>}]
    </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

UPDATE

A new problem is how can I differentiate one root entry of a chart from the root entry of another chart? Here's an example of 2 structures:

Structure 1:

<structure>
  <structentry id="1" parent="0" caller="true">Root 1</structentry>
  <structentry id="2" parent="1" caller="false">Child 1 of ID 1 (Root 1)</structentry>
  <structentry id="3" parent="1" caller="false">Child 2 of ID 1 (Root 1)</structentry>
  <structentry id="4" parent="1" caller="false">Child 3 of ID 1 (Root 1)</structentry>
  <structentry id="5" parent="4" caller="false">Child 1 of ID 4</structentry>
  <structentry id="6" parent="5" caller="false">Child 1 of ID 5</structentry>
</structure>

Structure 2:

<structure>
  <structentry id="7" parent="0" caller="true">Root 2</structentry>
  <structentry id="8" parent="7" caller="false">Child 1 of ID 7 (Root 2)</structentry>
  <structentry id="9" parent="7" caller="false">Child 2 of ID 7 (Root 2)</structentry>
  <structentry id="10" parent="7" caller="false">Child 3 of ID 7 (Root 2)</structentry>
  <structentry id="11" parent="10" caller="false">Child 1 of ID 10</structentry>
  <structentry id="12" parent="11" caller="false">Child 1 of ID 11</structentry>
</structure>
1

There are 1 best solutions below

10
On BEST ANSWER

Well, it's not very difficult to write an XSLT stylesheet that will produce the result you show:

XSLT 1.0

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

<xsl:key name="child" match="structentry" use="@parent" />

<xsl:template match="/structure">
    <xsl:apply-templates select="key('child', 0)"/>
</xsl:template>

<xsl:template match="structentry">
    <xsl:text>[{</xsl:text>
    <xsl:value-of select="."/>
    <xsl:text>}</xsl:text>
    <xsl:if test="@parent=0">,fill=white</xsl:if>
    <xsl:apply-templates select="key('child', @id)"/>
    <xsl:text>]</xsl:text>
</xsl:template>

</xsl:stylesheet>

Whether that fits in your overall scheme is hard for me to tell.


Added:

If your XML document has multiple structure elements, but the ids are unique document-wide, you can simply change the instruction:

<xsl:apply-templates select="key('child', 0)"/>

to:

<xsl:apply-templates select="key('child', 0, .)"/>

to limit the scope of the key to the current structure element. This is assuming your processor supports XSLT 2.0 or higher. In XSLT 1.0 you would need to use:

<xsl:apply-templates select="structentry[@parent=0]"/>

which is less efficient, since the parent values have already been indexed.


And of course you need to have:

<xsl:template match="structure">

without the leading /.