4

OK. I'm trying to work on communicating with the Pivotal Tracker API, which only returns data in an XML format. I have the following XML that I'm trying to deserialize into my domain model.


<?xml version="1.0" encoding="UTF-8"?>
<stories type="array" count="2" total="2">
  <story>
    <id type="integer">2909137</id>
    <project_id type="integer">68153</project_id>
    <story_type>bug</story_type>
    <url>http://www.pivotaltracker.com/story/show/2909137</url>
    <current_state>unscheduled</current_state>
    <description></description>
    <name>Test #2</name>
    <requested_by>Anthony Shaw</requested_by>
    <created_at type="datetime">2010/03/23 20:05:58 EDT</created_at>
    <updated_at type="datetime">2010/03/23 20:05:58 EDT</updated_at>
  </story>
  <story>
    <id type="integer">2909135</id>
    <project_id type="integer">68153</project_id>
    <story_type>feature</story_type>
    <url>http://www.pivotaltracker.com/story/show/2909135</url>
    <estimate type="integer">-1</estimate>
    <current_state>unscheduled</current_state>
    <description></description>
    <name>Test #1</name>
    <requested_by>Anthony Shaw</requested_by>
    <created_at type="datetime">2010/03/23 20:05:53 EDT</created_at>
    <updated_at type="datetime">2010/03/23 20:05:53 EDT</updated_at>
  </story>
</stories>

My 'story' object is created as follows:


public class story
{
     public int id { get; set; }
     public int estimate { get; set; }
     public int project_id { get; set; }

        public string story_type { get; set; }
        public string url { get; set; }
        public string current_state { get; set; }
        public string description { get; set; }
        public string name { get; set; }
        public string requested_by { get; set; }
        public string labels { get; set; }
        public string lighthouse_id { get; set; }
        public string lighthouse_url { get; set; }
        public string owned_by { get; set; }
        public string accepted_at { get; set; }
        public string created_at { get; set; }

        public attachment[] attachments { get; set; }
        public note[] notes { get; set; }
    }

When I execute my deserialization code, I receive the following exception:


Exception:
   There is an error in XML document (2, 2).

Inner Exception:
   <stories xmlns=''> was not expected.

I can deserialize the individual stories just fine, I just cannot deserialize this xml into an array of 'story' objects

And my deserialization code (value is a string of the xml)


var byteArray = Encoding.ASCII.GetBytes(value);
var stream = new MemoryStream(byteArray);
var deserializedObject = new XmlSerializer(typeof (story[])).Deserialize(stream)

Does anybody have any ideas?

1
  • 1
    Stack Overflow does not have a conflict-detection mechanism like Wikipedia has. If you're both editing at the same time, the more-recent edit will trump, and discard everything in the earlier edit. Commented Mar 24, 2010 at 0:33

5 Answers 5

11

Let me offer a more concise solution. Set your deserialization up to look like this:

var deserializedObject = new XmlSerializer(typeof(story[]), new XmlRootAttribute("stories")).Deserialize(stream);

By specifying that second parameter in the XmlSerializer, you can avoid having to stub out that class. It lets the serializer know what the root element's name is.

For this to work, the name of the class that represents the array-element type must exactly match the XML name, e.g. class story {}, <story>. You can get around this (and I'd recommend it as a best practice anyway) by specifying the XmlType:

[XmlType("story")]
public class Story
{
...
}

I prefer to do this as it frees me from being stuck with the XML type name.

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

3 Comments

This helped me out, a much neater way of doing things IMO.
+1 I had forgotten about the 2-arg version of XmlSerializer(), this answer is neat and clean.
I also agree with using XmlType(), I like to use it on most serialization that will be read/edited by humans to lowercase the elements, because I find it is easier to read when looking for data amidst the tag clutter than with standard .NET CamelCase / CamelCaps.
4

The problem is that you have no property named "stories". The XML Serializer has no idea what to do with the stories element when it sees it.

One thing you could try is to create a "stories" class:

public class stories : List<story> {}

and use

var byteArray = Encoding.ASCII.GetBytes(value);
stories deserializedObject  = null;
using (var stream = new MemoryStream(byteArray))
{
    var storiesSerializer = new XmlSerializer(typeof (stories));
    deserializedObject = (stories)storiesSerializer .Deserialize(stream);
}

2 Comments

gave it a shot, because I liked your implementation as it inheriting the List<story> the best, but it didn't work. thanks for giving it a shot tho :)
You can avoid having a separate class just for the array. See my example below.
2

Try something like

public class stories
{
    [XmlElement("story")]
    public story[] storyarray { get; set; }
}

...

var byteArray = Encoding.ASCII.GetBytes(value);
XmlSerializer serializer = new XmlSerializer(typeof(stories));
stories myStories = null;

using (var stream = new MemoryStream(byteArray))
{
    myStories = (stories)serializer.Deserialize(stream);
}

foreach (story stor in myStories.storyarray)
    Console.WriteLine(stor.story_type);

Edit: Updated code sample to use using statement based on feedback.

4 Comments

@Anthony Shaw: be sure to put the MemoryStream into a using block the way I did. Otherwise, you'll leak resources if an exception is thrown.
@AnthonyPegram: thanks but you also want myStories = (stories) serialiser.Deserialize(stream).
Good eye, John. I didn't think about that adjustment to the original code. And d'oh! That's what I get for editing code in the editor here rather than in the IDE!
You don't have to bother to stub out the root class only containing the array. I posted an example below.
1

XMSerializer expects an XML Namespace with which to understand your XML from.

xmlns="http://schemas.microsoft.com"

... ought to do. See the XML sample at the bottom of this page.

2 Comments

The XML I have is generated by the Pivotal Tracker API's RESTful service. I suppose if I had to I could edit it, but I'd rather not have to, since I'm using the deserialization method to do multiple classes, not just the story
Old question, but I have to comment here, respectfully. This answer is incorrect. XML Parsers do not need or use the namespace to parse or understand your XML, they only use it to declare unique namespaces for elements. Otherwise, element names can collide if mixing schemas/types. De-serialization is parsing. Even if we were talking XML validation, it would be looking at an XSD for that, not xmlns. You can decide to use whatever URI you want for a namespace, assuming it is unique. The fact that some companies use their URI for their namespaces is simply an artifact.
0

I would recommend that you generate an XSD from some sample XML you get from the web service. Then, with that XSD, you can generate the classes that have all the proper serialization attributes affixed to them.

  1. To generate a schema (unless you prefer to write your own), open the sample XML file in Visual Studio, and select the XML -> Create Schema menu option. Save that XSD.
  2. To generate the classes, run the XSD command from the VS command prompt. If you run it without parameters, it'll show you the command-line parameters you can use.
  3. Now you can create a type-safe XML serializer.

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.