user53885
user53885

Reputation: 3829

Is it possible in XSL to flatten XML hierarchy?

I have the following structure to XML file:

<INSTANCE>
  <Sections>
    <Section>
      <Forms>
        <Form>
          <Control id="GroupHeading1">
            <Property/>
            <Property/>
          </Control>
          <Control id="GroupHeading2">
           <Property/>
            <Control id="TextBox">
              <Property/>
              <Property/>
            </Control>
          </Control>
        </Form>
      </Forms>
    </Section>
  </Sections>
</INSTANCE>

I am trying to deserialize this into C# object, but I don't need to preserve the hierarchy (which is making it difficult for me to deserialize).

Is there XSL that can transform this to un-nest the Controls, and if possible add an attribute to any child Control with ParentId=""?

Thank you for any guidance!

Upvotes: 3

Views: 995

Answers (3)

Wayne
Wayne

Reputation: 60414

The following stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
    <!-- first-level control elements -->
    <xsl:template match="Control">
        <Control>
            <xsl:copy-of select="@*|*[not(self::Control)]" />
        </Control>
        <xsl:apply-templates select="Control" />
    </xsl:template>
    <!-- nested control elements -->
    <xsl:template match="Control/Control">
        <Control ParentId="{../@id}">
            <xsl:copy-of select="@*|*[not(self::Control)]" />
        </Control>
        <xsl:apply-templates select="Control" />
    </xsl:template>
</xsl:stylesheet>

Applied to the following document (same as original with one additional level of nesting for demonstration purposes):

<INSTANCE>
    <Sections>
        <Section>
            <Forms>
                <Form>
                    <Control id="GroupHeading1">
                        <Property />
                        <Property />
                    </Control>
                    <Control id="GroupHeading2">
                        <Property />
                        <Control id="TextBox">
                            <Property />
                            <Property />
                            <Control id="Grandchild">
                                <Property />
                            </Control>
                        </Control>
                    </Control>
                </Form>
            </Forms>
        </Section>
    </Sections>
</INSTANCE>

Produces an output with no nested <Control> elements:

<INSTANCE>
    <Sections>
        <Section>
            <Forms>
                <Form>
                    <Control id="GroupHeading1">
                        <Property />
                        <Property />
                    </Control>
                    <Control id="GroupHeading2">
                        <Property />
                    </Control>
                    <Control ParentId="GroupHeading2" id="TextBox">
                        <Property />
                        <Property />
                    </Control>
                    <Control ParentId="TextBox" id="Grandchild">
                        <Property />
                    </Control>
                </Form>
            </Forms>
        </Section>
    </Sections>
</INSTANCE>

Upvotes: 1

harpo
harpo

Reputation: 43168

This template should get you started. I ran it against .NET 2.0.

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

    <xsl:template match="*">
        <xsl:element name="{name()}">
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="*"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="Form">
        <Form>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="//Control"/>
        </Form>
    </xsl:template>

    <xsl:template match="Control">
        <Control>
            <xsl:if test="ancestor::Control/@id">
                <xsl:attribute name="ParentID"><xsl:value-of select="ancestor::Control/@id"/></xsl:attribute>
            </xsl:if>
            <xsl:copy-of select="*|@*"/>
        </Control>
    </xsl:template>

</xsl:stylesheet>

This is the output (indented for readability).

<INSTANCE>
    <Sections>
        <Section>
            <Forms>
                <Form>
                    <Control id="GroupHeading1">
                        <Property />
                        <Property />
                    </Control>
                    <Control id="GroupHeading2">
                        <Property />
                        <Control id="TextBox">
                            <Property />
                            <Property />
                        </Control>
                    </Control>
                    <Control ParentID="GroupHeading2" id="TextBox">
                        <Property />
                        <Property />
                    </Control>
                </Form>
            </Forms>
        </Section>
    </Sections>
</INSTANCE>

Upvotes: 1

Saurabh Gokhale
Saurabh Gokhale

Reputation: 46415

Given XML, the XmlSerializer can produce a graph of objects that hold the same instance data.
This is known as XML de-serialization

You need to look here :

Upvotes: 2

Related Questions