XSLT provides an elegant solution to this kind of requirement. As an example, here's a sample transform which works on the XML provided on this page and converts it to the json given on the same page.
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" />
<xsl:strip-space elements="*" />
<xsl:variable name="nl"><xsl:text>
</xsl:text></xsl:variable>
<xsl:variable name="q">"</xsl:variable>
<xsl:variable name="bInd" select="' '" />
<xsl:template match="glossary">
<xsl:value-of select="concat('{', $nl, $bInd, $q, local-name(.), $q, ' : {')" />
<xsl:apply-templates>
<xsl:with-param name="ind" select="concat($bInd, $bInd)" />
</xsl:apply-templates>
<xsl:value-of select="concat($nl, $bInd, '}', $nl, '}', $nl)" />
</xsl:template>
<xsl:template match="title">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, ' ', $q, local-name(.), $q, ' : ', $q, ., $q)" />
</xsl:template>
<!-- Other text elements -->
<xsl:template match="*">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, ', ', $q, local-name(.), $q, ' : ', $q, ., $q)" />
</xsl:template>
<xsl:template match="GlossEntry">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, $q, local-name(.), $q, ' : {')" />
<xsl:value-of select="concat($nl, $ind, $bInd, ' ',$q, 'ID', $q, ' : ', $q, @ID , $q)" />
<xsl:value-of select="concat($nl, $ind, $bInd, ', ', $q, 'SortAs', $q, ' : ', $q, @SortAs , $q)" />
<xsl:apply-templates>
<xsl:with-param name="ind" select="concat($ind, $bInd)" />
</xsl:apply-templates>
<xsl:value-of select="concat($nl, $ind, '}')" />
</xsl:template>
<xsl:template match="GlossDef">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, ', ', $q, local-name(.), $q, ' : {')" />
<xsl:value-of select="concat($nl, $ind, $bInd, ' ', $q, 'para', $q, ' : ', $q, para, $q)" />
<xsl:value-of select="concat($nl, $ind, $bInd, ', ', $q, 'GlossSeeAlso', $q, ' : [ ')" />
<xsl:for-each select="GlossSeeAlso">
<xsl:apply-templates select=".">
<xsl:with-param name="pos" select="position()" />
</xsl:apply-templates>
</xsl:for-each>
<xsl:value-of select="' ]'" />
<xsl:value-of select="concat($nl, $ind, '}')" />
</xsl:template>
<xsl:template match="GlossSeeAlso">
<xsl:param name="pos" />
<xsl:if test="$pos > 1">
<xsl:value-of select="', '" />
</xsl:if>
<xsl:value-of select="concat($q, @OtherTerm, $q)" />
</xsl:template>
<xsl:template match="GlossSee">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, ', ', $q, local-name(.), $q, ' : ', $q, @OtherTerm, $q)" />
</xsl:template>
<xsl:template match="GlossDiv | GlossList">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, ', ', $q, local-name(.), $q, ' : {')" />
<xsl:apply-templates>
<xsl:with-param name="ind" select="concat($ind, $bInd)" />
</xsl:apply-templates>
<xsl:value-of select="concat($nl, $ind, '}')" />
</xsl:template>
</xsl:transform>
This sample transform is XSLT 1.0. You can test it by adding a reference to it in the XML file, and loading the XML file in a web browser.
For example, if the above transform is stored in test.xsl, place the XML in test.xml with the transform referenced like this:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<glossary>
<title>example glossary</title>
<GlossDiv>
<title>S</title>
<GlossList>
<GlossEntry ID="SGML" SortAs="SGML">
<GlossTerm>Standard Generalized Markup Language</GlossTerm>
<Acronym>SGML</Acronym>
<Abbrev>ISO 8879:1986</Abbrev>
<GlossDef>
<para>A meta-markup language, used to create markup languages such as DocBook.</para>
<GlossSeeAlso OtherTerm="GML" />
<GlossSeeAlso OtherTerm="XML" />
</GlossDef>
<GlossSee OtherTerm="markup" />
</GlossEntry>
</GlossList>
</GlossDiv>
</glossary>