1

I have an xml file with a list like this:

<TestFile>
  <string>Foo</string>
  <bool>false</bool>
  <bool>true</bool>
  <string>Bar</string>
</TestFile>

I want to deserialize it to an Array of type "Value". That type has two subtypes "ValueString" and "ValueBool":

[XmlRoot("TestFile")]
public class TestFile
{
    public List<Test> Tests;
}

public class Value
{
}

public class ValueString : Value
{
    [XmlText]
    public string Value;
}

public class ValueBool : Value
{
    [XmlText]
    public bool Value;
}

I can't figure out how to do this. I've tried XmlIncludeAttributes but it's not enough because the element names do not match the class names. I've tried XmlChoiceIdentifier but the examples I found don't compile when I adapt them...

I need to preserve the order of the elements so separating the elements into two lists won't work. I also can't change the xml structure because it comes from an external source. Obviously it's just an example - my real types are more complex...

1
  • Personally I'd recommend writing your own xml parsing code for this as xml serialization really isn't intended as a way to read xml. Commented Apr 11, 2011 at 13:29

2 Answers 2

2

I finally stumbled over the solution:

[XmlRoot("TestFile")]
public class TestFile
{
    [XmlElement(ElementName = "string", Type = typeof(ValueString))]
    [XmlElement(ElementName = "bool", Type = typeof(ValueBool))]
    public List<Test> Tests;
}

I thought I tried that before... Well, at least it works now. There was another issue with this example: You can't have a boolean field mapped to an element text, but that specific issue isn't present in my true scenario anyway...

Sign up to request clarification or add additional context in comments.

Comments

2

Often we find ourselves having to transform the XML file given to us. Yes, it would be great if everyone subscribed to the same structure, but in a mid- to large-sized company this can be more difficult to achieve.

I started with your XML file:

<?xml version="1.0" encoding="utf-8" ?>
<TestFile>
  <string>Foo</string>
  <bool>false</bool>
  <bool>true</bool>
  <string>Bar</string>
</TestFile>

Then created the transform file (this is just an example and is all up to your own preference):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="@* | node()">
      <xsl:copy>
        <ParameterCollection>
          <xsl:apply-templates select="@* | node()"/>
        </ParameterCollection>
      </xsl:copy>
  </xsl:template>
  <xsl:template match="bool">
    <Parameter type="bool">
      <xsl:apply-templates select="node()"/>
    </Parameter>
  </xsl:template>
  <xsl:template match="string">
    <Parameter type="string">
      <xsl:apply-templates select="node()"/>
    </Parameter>
  </xsl:template>
</xsl:stylesheet>

Then I started piecing all the necessary classes together:

[XmlRootAttribute("TestFile", IsNullable = false)]
public class TestFile
{
    [XmlArrayAttribute("ParameterCollection")]
    public Parameter[] Parameters;
}

public class Parameter
{
    [XmlAttribute("type")]
    public string ObjectType;

    [XmlText]
    public string ObjectValue;
}

Then apply everything (hopefully in a more thoughtful manner than I have done):

class Program
{
    static void Main(string[] args)
    {
        FileInfo xmlFile = new FileInfo(@"Resources\TestFile.xml");
        FileInfo transformFile = new FileInfo(@"Resources\TestFileTransform.xslt");
        FileInfo prettyFile = new FileInfo(@"Resources\PrettyFile.xml");

        if (xmlFile.Exists && transformFile.Exists)
        {
            // Perform transform operations.
            XslCompiledTransform trans = new XslCompiledTransform();
            trans.Load(transformFile.FullName);
            trans.Transform(xmlFile.FullName, prettyFile.FullName);
        }

        if (prettyFile.Exists)
        {
            // Deserialize the new information.
            XmlSerializer serializer = new XmlSerializer(typeof(TestFile));
            XDocument doc = XDocument.Load(prettyFile.FullName);
            TestFile o = (TestFile)serializer.Deserialize(doc.CreateReader());

            // Show the results.
            foreach (Parameter p in o.Parameters)
            {
                Console.WriteLine("{0}: {1}", p.ObjectType, p.ObjectValue);
            }
        }

        // Pause for effect.
        Console.ReadKey();
    }
}

Hopefully this helps someone, or at least gives them another option. Typically, IMHO I would prefer to parse the file or stream, but that is just me.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.