views:

3254

answers:

13

This is my first time using XML Serialization and this is driving me absolutely nuts after 2 days of trying to troubleshoot this.

I get this error when the deserialization kicks in:

The XML element 'name' from namespace '' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.

The error happens on this line in my code:

Album album = (Album)serializer.Deserialize(reader);

I not sure why. There is no dup "name" node so I just don't get it. This is an XML doc received from an HttpWebResponse from a 3rd party REST API.

Here's the complete code:

My Album Class (the type I'm Deserializing to):

    public class Album
    {
        #region Constructors

        public Album() 
        { 

        }

        #endregion

        #region ElementConstants

        public static class ElementConstants
        {
            public const string aID = "aid";
            public const string Owner = "owner";
            public const string AlbumName = "name";
            public const string CoverPhotoID = "cover_pid";
            public const string CreateDate = "created";
            public const string LastModifiedDate = "modified";
            public const string Description = "description";
            public const string Location = "location";
            public const string AlbumURL = "link";
            public const string Size = "size";
            public const string Visible = "visible";
        }

        #endregion ElementConstants

        #region Public Properties

        [XmlArray(ElementName = "photos_GetAlbums_response")]
        [XmlArrayItem( "album" )]
        public Album[] Albums { get; set; }

        [XmlElement (ElementName = ElementConstants.AlbumName, DataType = "string")]
        public string AlbumID { get; set; }

        [XmlElement(ElementName = ElementConstants.aID, DataType = "int")]
        public Int32 CoverPhotoID { get; set; }

        [XmlElement(ElementName = ElementConstants.Owner, DataType = "string")]
        public string Owner { get; set; }

        [XmlElement(ElementName = ElementConstants.AlbumName, DataType = "string")]
        public string AlbumName { get; set; }

        [XmlElement(ElementName = ElementConstants.aID, DataType = "DateTime")]
        public DateTime CreateDate { get; set; }

        [XmlElement(ElementName = ElementConstants.LastModifiedDate, DataType = "DateTime")]
        public DateTime LastModifiedDate { get; set; }

        [XmlElement(ElementName = ElementConstants.Description, DataType = "string")]
        public string Description { get; set; }

        [XmlElement(ElementName = ElementConstants.Location, DataType = "string")]
        public string Location { get; set; }

        [XmlElement(ElementName = ElementConstants.AlbumURL, DataType = "string")]
        public string Link { get; set; }

        [XmlElement(ElementName = ElementConstants.Size, DataType = "size")]
        public string Size { get; set; }

        [XmlElement(ElementName = ElementConstants.Visible, DataType = "string")]
        public string Visible { get; set; }

        #endregion
    }

My Serializer Class:

    public class Serializer
    {
        public static Album CreateAlbumFromXMLDoc(XmlDocument doc)
        {
            // Create an instance of a serializer
            var serializer = new XmlSerializer(typeof(Album));
            var reader = new StringReader(doc.ToString());

            // Deserialize the Xml Object and cast to type Album
            Album album = (Album)serializer.Deserialize(reader);

            return album;
        }
    }

The XML that I am trying to Deserialized (copied from the Xml Doc object being passed into the CreateAlbumFromXMLDoc method when debugging in VS):

<?xml version="1.0" encoding="UTF-8"?>
<photos_GetAlbums_response xsi:schemaLocation="http://api.xxx.com/1.0/ http://api.xxx.com/1.0/xxx.xsd" list="true">
<album>
 <aid>3231990241086938677</aid>
 <cover_pid>7031990241087042549</cover_pid>
 <owner>1337262814</owner>
 <name>LA</name>
 <created>1233469624</created>
 <modified>1233469942</modified>
 <description>trip to LA</description>
 <location>CA</location>
 <link>http://www.xxx.com/album.php?aid=7333&amp;id=1337262814&lt;/link&gt;
 <size>48</size>
 <visible>friends</visible>
 </album>
<album>
 <aid>7031990241086936240</aid>
 <cover_pid>7031990241087005994</cover_pid>
 <owner>1337262814</owner>
 <name>Wall Photos</name>
 <created>1230437805</created>
 <modified>1233460690</modified>
 <description/>
 <location/>
 <link>http://www.xxx.com/album.php?aid=3296&amp;id=1337262814&lt;/link&gt;
 <size>34</size>
 <visible>everyone</visible>
 </album>
<album>
 <aid>7031990241086937544</aid>
 <cover_pid>7031990241087026027</cover_pid>
 <owner>1337262814</owner>
 <name>Mobile Uploads</name>
 <created>1231984989</created>
 <modified>1233460349</modified>
 <description/>
 <location/>
 <link>http://www.xxx.com/album.php?aid=6300&amp;id=1337262814&lt;/link&gt;
 <size>3</size>
 <visible>friends</visible>
 </album>
<album>
 <aid>7031990241086936188</aid>
 <cover_pid>7031990241087005114</cover_pid>
 <owner>1337262814</owner>
 <name>Christmas 2008</name>
 <created>1230361978</created>
 <modified>1230362306</modified>
 <description>My Album</description>
 <location/>
 <link>http://www.xxx.com/album.php?aid=5234&amp;id=1337262814&lt;/link&gt;
 <size>50</size>
 <visible>friends</visible>
 </album>
<album>
 <aid>7031990241086935881</aid>
 <cover_pid>7031990241087001093</cover_pid>
 <owner>1637262814</owner>
 <name>Hock</name>
 <created>1229889219</created>
 <modified>1229889235</modified>
 <description>Misc Pics</description>
 <location/>
 <link>http://www.xxx.com/album.php?aid=4937&amp;id=1637262814&lt;/link&gt;
 <size>1</size>
 <visible>friends-of-friends</visible>
 </album>
<album>
 <aid>7031990241086935541</aid>
 <cover_pid>7031990241086996817</cover_pid>
 <owner>1637262814</owner>
 <name>Test Album 2 (for work)</name>
 <created>1229460455</created>
 <modified>1229460475</modified>
 <description>this is a test album</description>
 <location/>
 <link>http://www.xxx.com/album.php?aid=4547&amp;id=1637262814&lt;/link&gt;
 <size>1</size>
 <visible>everyone</visible>
 </album>
<album>
 <aid>7031990241086935537</aid>
 <cover_pid>7031990241086996795</cover_pid>
 <owner>1637262814</owner>
 <name>Test Album (for work)</name>
 <created>1229459168</created>
 <modified>1229459185</modified>
 <description>Testing for work</description>
 <location/>
 <link>http://www.xxx.com/album.php?aid=4493&amp;id=1637262814&lt;/link&gt;
 <size>1</size>
 <visible>friends</visible>
 </album>
 </photos_GetAlbums_response>

A side note: Just for the hell of it, I paste that XML into XML Notepad 2007, it tells me:

Your XML document contains no xml-stylesheet processing instruction. To provide an XSLT transform, add the following to the top of your file and edit the href attribute accordingly:

I don't think that really means it's malformed or anything but just something to note.

So..

My ultimate goal is to get pass this damn error obviously and get an array of albums back using my code above once I can get past the error. I also want to make sure my code is correct in trying to retrieve that arrray back of albums using my Album[] property in my Album class or anything else I might be missing here. I think it's pretty close and should work but it's not.

+1  A: 

Well,

there is a link from Microsoft targeting your problem

Greco
so then after reviewing it's asking to change the schema. Well, I believe that means to change the XML doc format. We can't do that, we are getting the XML document from a third party API
CoffeeAddict
Wait, there is no duplicate. Each record has a name subnode. I don't get it.
CoffeeAddict
what is duplicated? There are several albums being returned here each with a name node...I don't see dups.
CoffeeAddict
@Dimi - Please read this article: Don't say "click here" - http://www.w3.org/QA/Tips/noClickHere:)
Simon Hartcher
will keep that in mind, thx!! :-)
Greco
+1  A: 

Yes - album is definitely not the root node in your XML.

What I would recommend you do is create a GetAlbumsResponse class which contains a list of albums, and move your deserialize code to the wrapper class.

Basically, remove the root element from your Album class definition, and :

  [XmlRoot (ElementName="GetAlbums_response")]
public class GetAlbumsResponse
{
    #region Constructors

    public GetAlbumsResponse()
    {

    }

    #endregion



    [XmlArray(ElementName="album")]
    public List<Album> Albums{get;set;}

    ... deserialization code...

}

markt
Thank you, I'm new to Serialization..so let me give that a try. Interesting.
CoffeeAddict
I don't know if I feel good about creating a class that's taking an album related property and shoving it into basically a non-album class. Hmm, let me think about this more.
CoffeeAddict
I actually have a constructor in my album class (which I did not show) that takes a param of HttpWebResponse. So essentially I'm already doing that first portion in my Album class via taking in the response
CoffeeAddict
And I'm taking that response in my album class and converting it to an XmL Doc right in that class. So I have it available locally in the Album class.
CoffeeAddict
I actually convert the response to an XmlDocument in the constructor
CoffeeAddict
Honestly, I don't understand why the Xml document should be in the Album class or why you would have a list of Albums in the Album class. From an OO perspective, why does an album have a reference to all albums.. ?
markt
Give me about 15 mins.. i'll put up a full code example.
markt
A: 

here's the rest that you are not seeing..the constructor with a param that provides the xml document. The General.GetResponseXmlDocument just converts the response to an xmlDoc

[XmlRoot (ElementName="album")]
public class Album
{
    #region Constructors
    public Album(HttpWebResponse response)
    {
        if (response == null)
            throw new NullReferenceException("The HttpWebResponse is null");

        _xmlDoc = General.GetResponseXmlDocument(response);

    }


    public Album() 
    { 

    }

    #endregion

    #region private fields

    private XmlDocument _xmlDoc;

    #endregion
CoffeeAddict
This should be a comment or an edit, not an answer.
Geoffrey Chetwood
+1  A: 

Ok - I coded up an example. I took a look at the Facebook API, now here is a FULL working example. Try this:

[XmlRoot("photos_getAlbums_response", Namespace="http://api.facebook.com/1.0/")]
public class GetAlbumsResponse
{
    public GetAlbumsResponse() 
    {    
    }

    [XmlElement("album")]
    public List<Album> Albums { get; set; }
}

public class Album
{
    [XmlElement("aid")]
    public long Aid{get;set;}

    [XmlElement("cover_pid")]
    public long CoverPid{get;set;}

    [XmlElement("owner")]
    public long Owner{get;set;}

    [XmlElement("name")]
    public string Name{get;set;}

    [XmlElement("created")]
    public long Created{get;set;}

    [XmlElement("modified")]
    public long Modified{get;set;}

    [XmlElement("description")]
    public string Description{get;set;}

    [XmlElement("location")]
    public string Location{get;set;}

    [XmlElement("link")]
    public string Link{get;set;}

    [XmlElement("size")]
    public int Size{get;set;}

    [XmlElement("visible")]
    public string Visible{get;set;}

    public Album()
    {}
}

class XmlUtils
{
    public static T DeserializeFromXml<T>(string xml)
    {
        T result;
        XmlSerializer ser = new XmlSerializer(typeof(T));
        using (TextReader tr = new StringReader(xml))
        {
            result = (T)ser.Deserialize(tr);
        }
        return result;
    }
}

Now.. with an xml photos_getAlbums_response from the Facebook API,

You can deserialize like this:

 GetAlbumsResponse response = XmlUtils.DeserializeFromXml<GetAlbumsResponse>(xmlResponseString);
markt
Thanks, I appreciate the example. Here's the thing that really bothers me. I don't want to create a class just for the sake of specifying the root outside Album. I mean in your example, it's close a bare class and I'd like put DeserializeResponseFromXml in a reuseable utility class.
CoffeeAddict
so basically i wonder if there is a way to move that property (generic list) back to inside my album class, move that DeserializeResponseFromXml to a utility class where I can reuse it across all my classes, and then somehow handle the root node in my album class with another attribute somewhere?
CoffeeAddict
I'm working on it..but like to keep the property within the album class...let me try some stuff I guess...
CoffeeAddict
The Response class is not "just for the sake of specifying the root ouside Album". You are deserializing from Xml and creating an object representation of the Response.But of course, it's your code, you can do whatever you like ;).
markt
In my opinion, having the list of Albums in the Album class is bad OO programming. Why should an Album have a list of all other albums ?When you generate that list, are you going to set the list in every object in the list ...?
markt
Some say that you can't deserialize directly into a Generic list and must first deserialize into an array since XmlSerializer won't serialize directly to a list..it's not possible.Second, I'm staring a new thread because I've done some things different since then.
CoffeeAddict
Whoever told you that you can't deserialize into a generic list doesn't know what they are talking about. The code I gave you works fine.
markt
I modified this answer into a full working example for your Facebook Xml response.
markt
+1  A: 

You cannot serialize to a Generic list like that, it just be an array.

CoffeeAddict
The code above works. So apparently you can. :)
markt
+5  A: 

Personally, I wouldn't use constants here - they make it hard to spot errors (and since you probably aren't re-using them, don't add much). For example:

    [XmlElement (ElementName = ElementConstants.AlbumName, DataType = "string")]
    public string AlbumID { get; set; }
...
    [XmlElement(ElementName = ElementConstants.AlbumName, DataType = "string")]
    public string AlbumName { get; set; }

Looks suspect to me...

An easier approach is to write the xml you want to a file (foo.xml, say) and use:

xsd foo.xml
xsd foo.xsd /classes

Then look at foo.cs.

Marc Gravell
I agree, run it through the xsd tool!
Anthony Mastrean
Mark, any recollection why these posts were not closed/deleted as duplicates?
John Saunders
I'd guess: not enough votes... it pre-dates when I could force the issue, but I also seem to recall different information being revealed in each.
Marc Gravell
+3  A: 

Here we go... note the xml was invalid (& should be &amp;; use of undeclared xsi namespace-alias). Note also that I added an enum for the visibility, added handling for converting the long to DateTime, and added the wrapper type:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

static class Program
{
    const string xml = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<photos_GetAlbums_response
    xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
    xsi:schemaLocation=""http://api.xxx.com/1.0/ http://api.xxx.com/1.0/xxx.xsd""
    list=""true"">
<album>
 <aid>3231990241086938677</aid>
 <cover_pid>7031990241087042549</cover_pid>
 <owner>1337262814</owner>
 <name>LA</name>
 <created>1233469624</created>
 <modified>1233469942</modified>
 <description>trip to LA</description>
 <location>CA</location>
 <link>http://www.xxx.com/album.php?aid=7333&amp;amp;id=1337262814&lt;/link&gt;
 <size>48</size>
 <visible>friends</visible>
 </album>
<album>
 <aid>7031990241086936240</aid>
 <cover_pid>7031990241087005994</cover_pid>
 <owner>1337262814</owner>
 <name>Wall Photos</name>
 <created>1230437805</created>
 <modified>1233460690</modified>
 <description/>
 <location/>
 <link>http://www.xxx.com/album.php?aid=3296&amp;amp;id=1337262814&lt;/link&gt;
 <size>34</size>
 <visible>everyone</visible>
 </album>
<album>
 <aid>7031990241086937544</aid>
 <cover_pid>7031990241087026027</cover_pid>
 <owner>1337262814</owner>
 <name>Mobile Uploads</name>
 <created>1231984989</created>
 <modified>1233460349</modified>
 <description/>
 <location/>
 <link>http://www.xxx.com/album.php?aid=6300&amp;amp;id=1337262814&lt;/link&gt;
 <size>3</size>
 <visible>friends</visible>
 </album>
<album>
 <aid>7031990241086936188</aid>
 <cover_pid>7031990241087005114</cover_pid>
 <owner>1337262814</owner>
 <name>Christmas 2008</name>
 <created>1230361978</created>
 <modified>1230362306</modified>
 <description>My Album</description>
 <location/>
 <link>http://www.xxx.com/album.php?aid=5234&amp;amp;id=1337262814&lt;/link&gt;
 <size>50</size>
 <visible>friends</visible>
 </album>
<album>
 <aid>7031990241086935881</aid>
 <cover_pid>7031990241087001093</cover_pid>
 <owner>1637262814</owner>
 <name>Hock</name>
 <created>1229889219</created>
 <modified>1229889235</modified>
 <description>Misc Pics</description>
 <location/>
 <link>http://www.xxx.com/album.php?aid=4937&amp;amp;id=1637262814&lt;/link&gt;
 <size>1</size>
 <visible>friends-of-friends</visible>
 </album>
<album>
 <aid>7031990241086935541</aid>
 <cover_pid>7031990241086996817</cover_pid>
 <owner>1637262814</owner>
 <name>Test Album 2 (for work)</name>
 <created>1229460455</created>
 <modified>1229460475</modified>
 <description>this is a test album</description>
 <location/>
 <link>http://www.xxx.com/album.php?aid=4547&amp;amp;id=1637262814&lt;/link&gt;
 <size>1</size>
 <visible>everyone</visible>
 </album>
<album>
 <aid>7031990241086935537</aid>
 <cover_pid>7031990241086996795</cover_pid>
 <owner>1637262814</owner>
 <name>Test Album (for work)</name>
 <created>1229459168</created>
 <modified>1229459185</modified>
 <description>Testing for work</description>
 <location/>
 <link>http://www.xxx.com/album.php?aid=4493&amp;amp;id=1637262814&lt;/link&gt;
 <size>1</size>
 <visible>friends</visible>
 </album>
 </photos_GetAlbums_response>";
    static void Main()
    {
        XmlSerializer ser = new XmlSerializer(typeof(GetAlbumsResponse));
        GetAlbumsResponse response;
        using (StringReader reader = new StringReader(xml))
        {
            response = (GetAlbumsResponse)ser.Deserialize(reader);
        }

    }
}

[Serializable, XmlRoot("photos_GetAlbums_response")]
public class GetAlbumsResponse
{
    [XmlElement("album")]
    public List<Album> Albums {get;set;}

    [XmlAttribute("list")]
    public bool IsList { get; set; }
}
public enum AlbumVisibility
{
    [XmlEnum("")]
    None,
    [XmlEnum("friends")]
    Friends,
    [XmlEnum("friends-of-friends")]
    FriendsOfFriends,
    [XmlEnum("everyone")]
    Everyone

}
[Serializable]
public class Album
{
    static readonly DateTime epoch = new DateTime(1970, 1, 1);
    static long SerializeDateTime(DateTime value)
    {
        return (long)((value - epoch).TotalSeconds);
    }
    static DateTime DeserializeDateTime(long value)
    {
        return epoch.AddSeconds(value);
    }
    [XmlElement("aid")]
    public long AlbumID { get; set; }

    [XmlElement("cover_pid")]
    public long CoverPhotoID { get; set; }

    [XmlElement("owner")]
    public long Owner { get; set; }

    [XmlElement("name")]
    public string AlbumName { get; set; }

    [XmlIgnore]
    public DateTime CreateDate { get; set; }

    [XmlElement("created"), Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public long CreateDateInt64 {
        get {return SerializeDateTime(CreateDate);}
        set {CreateDate = DeserializeDateTime(value);}
    }

    [XmlIgnore]
    public DateTime LastModifiedDate { get; set; }

    [XmlElement("modified"), Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public long LastModifiedDateInt64
    {
        get { return SerializeDateTime(LastModifiedDate); }
        set { LastModifiedDate = DeserializeDateTime(value); }
    }

    [XmlElement("description")]
    public string Description { get; set; }

    [XmlElement("location")]
    public string Location { get; set; }

    [XmlElement("link")]
    public string Link { get; set; }

    [XmlElement("size")]
    public int Size { get; set; }

    [XmlElement("visible")]
    public AlbumVisibility Visibility { get; set; }
}
Marc Gravell
+1  A: 

I do not have control over the incoming XML...it's from Facebook. I mean others are using this just fine outside of .NET. So even if it's missing what you say, it has to work somehow in .NET Deserialization

CoffeeAddict
>>>note the xml was invalid (
CoffeeAddict
I don't think I need xmls attribute necessarily?
CoffeeAddict
And is is really necessary to create a whole new class for the root node? Can't I just decorate with:[XmlArray(ElementName = "photos_GetAlbums_response")][XmlArrayItem( "album" )]
CoffeeAddict
Marc Gravell
If the xml isn't well-formed, then you'll have to fix it first... perhaps some simple string-replace would fix both issues.
Marc Gravell
Now I get this error:Data at the root level is invalid. Line 1, position 1.
CoffeeAddict
>>>Have you switched identity?Not following what you mean by identity
CoffeeAddict
correction, the output I posted...the xml was showing XSL. the original XML doc does have the xmlns...so my last error is a different problem
CoffeeAddict
I know what the problems are; I'll add a new reply...
Marc Gravell
+1  A: 

The Xml that would work for your current code is something like this:

<Album><photos_GetAlbums_response>
<Album>
   <photos_GetAlbums_response>
      <Album>
         <photos_GetAlbums_response> ....

A response, which has an array of Albums, where each Album has a response which is an Array of Albums...etc.

Anyway, I already helped you in your other question, and even went to the trouble of creating a full working code sample. Why did you create another question for the same problem ?

markt
A: 

Follow-up. I've been pulling my hair out since then.

Here's the latest. I did not use some things for now (from Marc) like the Enum, etc. I might change that later. I also pulled out the datetime stuff as it just looked wierd and I did not get errors on that anway without...at least yet. The main problem now is still my damn XML.

It's still appearing to have problems with the format I guess? Unless it's covering up another problem, no clue. This is driving me fing crazy.

I now get this error when the deserialization kicks in:

Data at the root level is invalid. Line 1, position 1.

The error happens on this line in my code: GetAlbumsResponse album = (GetAlbumsResponse)serializer.Deserialize(reader);

How I get the response into an XmL doc:

public static XmlDocument GetResponseXmlDocument(HttpWebResponse response)
        {
            Stream dataStream = null; // stream from WebResponse
            XmlDocument doc = new XmlDocument();

            if (doc == null)
            {
                throw new NullReferenceException("The web reponse was null");
            }

            // Get the response stream so we can read the body of the response
            dataStream = response.GetResponseStream();

            // Open the stream using a StreamReader for easy access
            StreamReader reader = new StreamReader(dataStream);

            // Load response into string variable so that we can then load into an XML doc
            string responseString = reader.ReadToEnd();

            // Create an XML document & load it with the response data
            doc.LoadXml(responseString);

            // Final XML document that represents the response
            return doc;
        }

My Album Class & Root Level Class (thanks to help from Marc..I get it now):

namespace xxx.Entities
{

    [Serializable, XmlRoot("photos_GetAlbums_response")]
    public class GetAlbumsResponse
    {
        [XmlElement("album")]
        public List<Album> Albums { get; set; }

        [XmlAttribute("list")]
        public bool IsList { get; set; }
    }

    public class Album
    {
        #region Constructors

        public Album()
        {

        }

        #endregion

        #region ElementConstants

        /// <summary>
        /// Constants Class to eliminate use of Magic Strings (hard coded strings)
        /// </summary>
        public static class ElementConstants
        {
            public const string aID = "aid";
            public const string Owner = "owner";
            public const string AlbumName = "name";
            public const string CoverPhotoID = "cover_pid";
            public const string CreateDate = "created";
            public const string LastModifiedDate = "modified";
            public const string Description = "description";
            public const string Location = "location";
            public const string AlbumURL = "link";
            public const string Size = "size";
            public const string Visible = "visible";
        }

        #endregion ElementConstants

        #region Public Properties

        [XmlElement (ElementName = ElementConstants.aID, DataType = "string")]
        public string AlbumID { get; set; }

        [XmlElement(ElementName = ElementConstants.CoverPhotoID, DataType = "int")]
        public Int32 CoverPhotoID { get; set; }

        [XmlElement(ElementName = ElementConstants.Owner, DataType = "string")]
        public string Owner { get; set; }

        [XmlElement(ElementName = ElementConstants.AlbumName, DataType = "string")]
        public string AlbumName { get; set; }

        public string Created { get; set; }

        public DateTime Modified { get; set; }

        [XmlElement(ElementName = ElementConstants.Description, DataType = "string")]
        public string Description { get; set; }

        [XmlElement(ElementName = ElementConstants.Location, DataType = "string")]
        public string Location { get; set; }

        [XmlElement(ElementName = ElementConstants.AlbumURL, DataType = "string")]
        public string Link { get; set; }

        public string Size { get; set; }

        [XmlElement(ElementName = ElementConstants.Visible, DataType = "string")]
        public string Visible { get; set; }

        #endregion
    }
}

My Serializer Class:

namespace xxx.Utilities
{
    public class Serializer
    {
        public static List<Album> CreateAlbumFromXMLDoc(XmlDocument doc)
        {
            // Create an instance of a serializer
            var serializer = new XmlSerializer(typeof(Album));
            var reader = new StringReader(doc.ToString());

            // Deserialize the Xml Object and cast to type Album
            GetAlbumsResponse album = (GetAlbumsResponse)serializer.Deserialize(reader);

            return album.Albums;
        }
    }
}

The true XML incoming, that I am trying to Deserialize (yes it does have xmlns):

<?xml version="1.0" encoding="UTF-8"?>
<photos_GetAlbums_response xmlns="http://api.xxx.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://api.xxx.com/1.0/ http://api.xxx.com/1.0/xxx.xsd" list="true">
  <album>
    <aid>7321990241086938677</aid>
    <cover_pid>7031990241087042549</cover_pid>
    <owner>1124262814</owner>
    <name>Album Test 1</name>
    <created>1233469624</created>
    <modified>1233469942</modified>
    <description>Our trip</description>
    <location>CA</location>
    <link>http://www.xxx.com/album.php?aid=7733&amp;id=1124262814&lt;/link&gt;
    <size>48</size>
    <visible>friends</visible>
  </album>
  <album>
    <aid>231990241086936240</aid>
    <cover_pid>7042330241087005994</cover_pid>
    <owner>1124262814</owner>
    <name>Album Test 2</name>
    <created>1230437805</created>
    <modified>1233460690</modified>
    <description />
    <location />
    <link>http://www.xxx.com/album.php?aid=5296&amp;id=1124262814&lt;/link&gt;
    <size>34</size>
    <visible>everyone</visible>
  </album>
  <album>
    <aid>70319423341086937544</aid>
    <cover_pid>7032390241087026027</cover_pid>
    <owner>1124262814</owner>
    <name>Album Test 3</name>
    <created>1231984989</created>
    <modified>1233460349</modified>
    <description />
    <location />
    <link>http://www.xxx.com/album.php?aid=6600&amp;id=1124262814&lt;/link&gt;
    <size>3</size>
    <visible>friends</visible>
  </album>
</photos_GetAlbums_response>
CoffeeAddict
"1,1" suggests that your xml file starts with a newline. The "<?xml..." should be on the very first line. If using a string literal in C#, it should be @"<?xml ...", not the <?xml on the next line.
Marc Gravell
Marc, I edited the last post to show how I'm reading the response and shoving it into that XmlDoc.I don't see how a newline got introduced.
CoffeeAddict
I do not see a line before <?xml when copying and pasting from the xml I get when debugging and looking at the XmlDoc.
CoffeeAddict
It seems to me that the reader is only 22 chars long with the value "System.Xml.XmlDocument". I looked at the identity and it's null in the reader. I am not familiar with Identity in this case.
CoffeeAddict
Tried to use doc.OuterXml instead of doc.ToString() but that produce an altogether different error..so not sure what the hell I'm doing wrong here.
CoffeeAddict
There definitely is not a newline. When I look at the doc, it's all one line of XML.
CoffeeAddict
I know what the problems are; I'll add a new reply...
Marc Gravell
This should be a comment or an edit, not an answer.
Geoffrey Chetwood
A: 

I'm starting to think I'm formatting something wrong. I've seen out there to never treat XML as a String that needs to be deserialized (check this out http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/22748e41-6953-4031-b5f8-ad6814ec2564/). So never do XmlDoc.ToString() for example.

So I am wondering about what I'm passing into my Deserialization method. Then I take that and do a ToString() and that string is passed to the Deserialization method. This "might" be my problem but not sure.

I'm passing in an XmlDocument. First, this MSDN article doesn't show that Deserialization can perform on a string:

http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.deserialize(VS.71).aspx

So what I tried is to change up my GetResponse method that's processing the HttpWebResponse to return an XmlTtextReader instead since that's one of the valid types listed in that msdn doc that says the Deserializer can work with:

public static XmlTextReader GetResponseXmlTextReader(HttpWebResponse response)
{
    Stream dataStream = null; // stream from WebResponse

    // Get the response stream so we can read the body of the response
    dataStream = response.GetResponseStream();

    XmlTextReader xmlTextReader = new XmlTextReader(dataStream);

    if (xmlTextReader == null)
    {
        throw new NullReferenceException("The XmlTextReader is null");
    }

    return xmlTextReader;
}

Then pass in an XmlTextReader to my Deserialization Method:

public class Serializer
{
    public static List<Album> CreateAlbumFromXMLDoc(XmlTextReader reader)
    {
        // Create an instance of a serializer
        var serializer = new XmlSerializer(typeof(Album));

        GetAlbumsResponse album = (GetAlbumsResponse)serializer.Deserialize(reader);

        return album.Albums;
    }
}

Unfortunately I found that the reader was null. Not sure why. It's almost the same method I had prior when I stuffed the response stream into an XmlDoc.

I get "System.Xml.XmlException: Root element is missing." because the reader doesn't have any data I think once I get to this line:

GetAlbumsResponse album = (GetAlbumsResponse)serializer.Deserialize(reader);

CoffeeAddict
I know what the problems are; I'll add a new reply...
Marc Gravell
BTW - the forum post about xml/strings is very wrong; BOMs ("leading byte markers" in the forum) relate to the **encoding** - i.e. the file storage. A string is independent of BOMs, and doesn't have these concerns until you choose to serialize it (again, via an encoding).
Marc Gravell
This should be a comment or an edit, not an answer.
Geoffrey Chetwood
+1  A: 

Use System.Xml.XmlDocument to parse the input. It shouldn't take more than an hour to write the code to extract the data yourself.

+2  A: 

(08 Feb) First, treating xml as a string (for reading) isn't going to cause errors.

The problem is the namespace (the xmlns without the xsi); this wasn't in the earlier xml, so I couldn't include it... basically, you need to tell the serializer abouut it:

[Serializable, XmlRoot("photos_GetAlbums_response",
    Namespace="http://api.xxx.com/1.0/")]
public class GetAlbumsResponse { /* code as before */ }

[Serializable, XmlType(Namespace="http://api.xxx.com/1.0/")]
public class Album { /* code as before */ }

On this occasion, a constant for the namespace would make sense (since you are re-using it).

If the xml you are showing is accurate, then the links are still corrupt, though... but maybe this is just copy/paste (i.e. don't apply this change until you know it errors...): you need &amp; (not &). Suggest some "Replace"... at the crudest level:

string fixedXml = xml.Replace("&", "&amp;");

(although something more precise might be better - perhaps a regex)

Note that with the different data I also had to make some of the data strings (rather than long):

[XmlElement("aid")]
public string AlbumID { get; set; }

[XmlElement("cover_pid")]
public string CoverPhotoID { get; set; }

[XmlElement("owner")]
public string Owner { get; set; }

With these changes (and mostly my original code) it works.

Of course, by this point you should be thinking "I wish I'd used xsd".

Marc Gravell