3

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!

iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202
user53885
  • 3,809
  • 11
  • 33
  • 43
  • A bit duplicate : http://stackoverflow.com/questions/5025365/serialize-and-deserialize-xml-using-dot-net-c – Saurabh Gokhale Mar 26 '11 at 18:42
  • @user53885: Without desired output this is not a complete question. –  Mar 26 '11 at 22:43
  • @user53885 - Given your comment on @harpo's answer -- "I was thinking of instead of having a control underneath any other control that for example TextBox would have an attribute of ParentId="GroupHeading2", and be at the same level as the GroupHeadings." -- I've added a solution in which all `` elements appear at the same level. – Wayne Mar 26 '11 at 22:57
  • @Alejandro - Explicit example output would be best, but OP does give more clues than others as to the desired output – Wayne Mar 26 '11 at 22:58
  • 1
    @lwburk: There are four hours of comments describing requeriments... –  Mar 26 '11 at 23:04

3 Answers3

2

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 :

Saurabh Gokhale
  • 53,625
  • 36
  • 139
  • 164
1

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>
harpo
  • 41,820
  • 13
  • 96
  • 131
  • It won't let me edit. But I was thinking of instead of having a control underneath any other control that for example TextBox would have an attribute of ParentId="GroupHeading2", and be at the same level as the GroupHeadings. – user53885 Mar 26 '11 at 19:00
  • What won't let you edit? If you mean you can't rewrite the template file in-place, you can always transform it in memory prior to deserialization. – harpo Mar 26 '11 at 19:08
  • Sorry I meant the question itself I posted here. It wont let me update as you requested I do. – user53885 Mar 26 '11 at 19:10
  • Look at the output again. That is what you get. The Control templates are wrapped inside of another Control element which has the ParentID element where appropriate. But all three controls from the source are at the same level. There are of course many ways to format the output, but without any specification, I just did what I thought was reasonable. Try the transform yourself and adjust it to your liking. Just about any kind of XSLT-based flattening solution is going to look something like this. – harpo Mar 26 '11 at 19:13
  • Thank you!!! I took the tag out of line 11 from what you sent, and it pretty much does exactly what I need - except none of the output has ParentId's in it. I will try and figure out how to get the ParentId part working. – user53885 Mar 26 '11 at 19:26
  • That's why I wrapped the extra element. Using copy-of will create a literal copy of the Control element from the source, and I don't know any way to insert an attribute into this. You'd probably have to do a literal copy of the child elements... I'll update the template. – harpo Mar 26 '11 at 19:31
  • Thank you, I am going to accept your excellent answer, just didn't want to accept(in case it doesnt let you edit) if what I said you wanted to add anything. – user53885 Mar 26 '11 at 19:35
  • @user53885, I made one little change to the **select** in the **copy-of**. This copies the child elements and attributes instead of the Control template itself. This may or may not work with text content in the control... I didn't test that. – harpo Mar 26 '11 at 19:36
  • Is there any way to maintain the original structure above this that wrapped it? Like this part
    when I run this all of that is gone. I have multiple sections and multiple forms.
    – user53885 Mar 26 '11 at 19:47
  • @user53885, see update. This will copy all elements literally except the Form, which will use the flattened Control list from the original version. Beyond this, I think you're in the scope of another question :) – harpo Mar 26 '11 at 19:55
  • Oops, I was a little too hasty there. – harpo Mar 26 '11 at 20:04
  • @user53885, okay, I fixed the template to do what I said before. Got a little careless :) – harpo Mar 26 '11 at 20:09
  • @user53885: This use an strange "identity" rule and a descendant axis without need. Check @lwburk's [answer](http://stackoverflow.com/questions/5444342/is-it-possible-in-xsl-to-flatten-xml-heirarchy/5445863#5445863) for proper XSLT style. –  Mar 26 '11 at 23:03
1

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>
Wayne
  • 59,728
  • 15
  • 131
  • 126