Message contract wrappername and message body name attribute does not work - c#

I have couple of classes which define as message contract and properties define as message body. I specified wrappername and name attribute respectively. It works for parent class but does not work for child class.
Classes:
[MessageContract(WrapperName = "ticketstatus",IsWrapped = true)]
public class TicketStatusResponse
{
[MessageBodyMember(Name="status")]
public string Status { get; set; }
[MessageBodyMember(Name="errorcode")]
public string ErrorCode { get; set; }
[MessageBodyMember(Name="errormessage")]
public string ErrorMessage { get; set; }
[MessageBodyMember(Name="tasks")]
public List<Task> Tasks { get; set; }
}
[MessageContract(WrapperName="task")]
public class Task
{
[MessageBodyMember(Name="taskname")]
public string TaskName { get; set; }
[MessageBodyMember(Name="complete")]
public string Complete { get; set; }
}
Response info:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<ticketstatus xmlns="http://tempuri.org/">
<errorcode>0</errorcode>
<errormessage/>
<status>Pending</status>
<tasks xmlns:a="http://schemas.datacontract.org/2004/07/MessageContractExample.Contracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Task>
<a:Complete>1</a:Complete>
<a:TaskName>task1</a:TaskName>
</a:Task>
<a:Task>
<a:Complete>2</a:Complete>
<a:TaskName>task2</a:TaskName>
</a:Task>
</tasks>
<ticketno>567890</ticketno>
<waittime>4</waittime>
</ticketstatus>
</s:Body>
</s:Envelope>
My expected xml tag name of "Task" should be as follows because I change name using wrappername and name attribute:
<a:task>
<a:complete>2</a:complete>
<a:taskname>task2</a:taskName>
</a:task>
But i am getting
<a:Task>
<a:Complete>1</a:Complete>
<a:TaskName>task1</a:TaskName>
</a:Task>
Why wrapper name and name attribute do not work for child node. How can I fix node name?
Any idea or hints?

Have you considered IsWrapped = true as that is the difference between the classes.

Related

XML API Request returns "d3p1" for arrays or sub-classes

Using Postman, C#, .NET Framework 4.7
I make a GET request from Postman with content-accept set to "application/xml" and my .NET Framework Web API will respond with XML (brilliant!). I have not needed to serialize anything manually as .NET Framework serializes my response object with when I return Request.CreateResponse(HttpStatusCode.OK, myResponse).
However, it seems to include extra things which I am not familiar with and I am wondering if they can be removed via Global.asax settings or similar?
xmlns="http://schemas.datacontract.org/2004/07/dto.MyModels" is something I would like to remove!
"d3p1" is not something I have come across when creating XML. Can I remove it somehow?
<error i:nil="true" /> pops up when something is null. Is it possible to just be or just not appear when it is null?
So, The XML response looks like this:
<SimpleResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/dto.MyModels">
<error i:nil="true" />
<result>
<bookList xmlns:d3p1="http://schemas.datacontract.org/2004/07/dto.MyModels.Shared">
<d3p1:Book>
<d3p1:bookname>Book 1 Name</d3p1:bookname>
<d3p1:serial>ZMeTaQ1kejh0mJYGHE4+1a4Y2juU6tMDd5zYDqN4tqI=</d3p1:serial>
<d3p1:id>4</d3p1:id>
</d3p1:Book>
<d3p1:Book>
<d3p1:bookname>Hello World</d3p1:bookname>
<d3p1:serial>9lM16kho3bgsrG+wRh4ejtZjwrYJwp6FbRqnnZ4CJPA=</d3p1:serial>
<d3p1:id>5</d3p1:id>
</d3p1:Book>
<d3p1:Book>
<d3p1:bookname>Ding</d3p1:bookname>
<d3p1:serial>XCqqKB+Wi3i4z6nN1Ry8IHtar6ogojjiqxMfvfgC0qc=</d3p1:serial>
<d3p1:id>6</d3p1:id>
</d3p1:Book>
</bookList>
<author xmlns:d3p1="http://schemas.datacontract.org/2004/07/dto.Models.Android.Shared">
<d3p1:pictureId>0</d3p1:pictureId>
<d3p1:websiteurl i:nil="true" />
<d3p1:email i:nil="true" />
<d3p1:name>Jo Blogs</d3p1:size>
<d3p1:age>0</d3p1:size>
</author>
</result>
</SimpleResponse>
My classes look something like this:
public string error { get; set;}
public class SimpleResponse
{
public List<Book> bookList { get; set; }
public Author author { get; set; }
}
public class Book
{
public string bookname { get; set; }
public string serial { get; set; }
public int id { get; set; }
}
public class Author
{
public string pictureId{ get; set; }
public string websiteurl { get; set; }
public string email { get; set; }
public string name { get; set; }
public int age { get; set; }
}
xmlns="http://schemas.datacontract.org/2004/07/dto.MyModels" is something I would like to remove!
"d3p1" is not something I have come across when creating XML. Can I remove it somehow?
The attributes:
xmlns="http://schemas.datacontract.org/2004/07/dto.MyModels"
xmlns:d3p1="http://schemas.datacontract.org/2004/07/dto.MyModels.Shared"
are both XML namespace declarations. The first establishes a default XML namespace for your XML document; the second declares a namespace to which all child elements prefaced with d3p1: belong.
From the specific namespace chosen, it's apparent you are using DataContractSerializer for XML serialization. This serializer assigns XML namespaces according to rules documented in Data Contract Names: Data Contract Namespaces
By default, any given CLR namespace (in the format Clr.Namespace) is mapped to the namespace http://schemas.datacontract.org/2004/07/Clr.Namespace. To override this default, apply the ContractNamespaceAttribute attribute to the entire module or assembly. Alternatively, to control the data contract namespace for each type, set the Namespace property of the DataContractAttribute.
Since you don't want an XML namespace for any of your types, the first option would appear to be the easiest: apply the following attributes to your assembly:
[assembly: ContractNamespaceAttribute("", ClrNamespace = "dto.MyModels")]
[assembly: ContractNamespaceAttribute("", ClrNamespace = "dto.MyModels.Shared")]
[assembly: ContractNamespaceAttribute("", ClrNamespace = "dto.Models.Android.Shared")]
Alternatively, you could apply data contract attributes to your types:
[DataContract(Name = "Book", Namespace = "")]
public class Book
{
[DataMember]
public string bookname { get; set; }
[DataMember]
public string serial { get; set; }
[DataMember]
public int id { get; set; }
}
Note that data contract serialization is opt-in so you will need to apply [DataMember] to each member to be serialized.
As a final alternative, you could switch to using XmlSerializer which has no default XML namespace. To do that you will need to apply [XmlSerializerFormat] to your service contract as shown in Manually Switching to the XmlSerializer.
<error i:nil="true" /> pops up when something is null. Is it possible to just be or just not appear when it is null?
These null elements can be removed by setting DataMemberAttribute.EmitDefaultValue on reference type data members as shown in this answer to Can I configure the DataContractSerializer to not create optional (i.e. Nullable<> and List<>) elements in output XML? by Darren Clark:
[DataContract(Name = "Book", Namespace = "")]
public class Book
{
[DataMember(EmitDefaultValue = false)]
public string bookname { get; set; }
[DataMember(EmitDefaultValue = false)]
public string serial { get; set; }
[DataMember]
public int id { get; set; }
}
Alternatively you could switch to XmlSerializer which does not output null reference values by default.

C# web service - getting list of equal element names

I was creating C# SOAP web service and web method from interface specification that should have specific xml structure in request:
<SomeItems>
<Value></Value>
<Value></Value>
...
</SomeItems>
It needs to have list of elements with equal names.
When I put in C# code class like:
public List<int> SomeItems { get; set; }
I get as output (checked in SOAP UI) (epv is xmlns:epv="..."):
<epv:SomeItems>
<!--Zero or more repetitions:-->
<epv:int></epv:int>
</epv:SomeItems>
If I add another class named "Value":
public List<Value> SomeItems { get; set; }
public class Value
{
public int Value1 { get; set; }
}
I get in SOAP UI:
<epv:SomeItems>
<!--Zero or more repetitions:-->
<epv:Value>
<epv:Value1></epv:Value1>
</epv:Value>
</epv:SomeItems>
Is there any way to get:
<epv:SomeItems>
<!--Zero or more repetitions:-->
<epv:Value></epv:Value>
</epv:SomeItems>
Where "Value" is int.
Moved solution from question to answer:
EDIT:
I have found solution:
public SomeItems SomeItems{ get; set; }
[XmlType(AnonymousType = true)]
public class SomeItems
{
[XmlElement(ElementName = "Value")]
public List<int> Value { get; set; }
}
Finally in SOAP UI:
<epv:SomeItems>
<!--Zero or more repetitions:-->
<epv:Value>?</epv:Value>
</epv:SomeItems>

WCF MessageHeader attribute does not deserialized

I am developing the the SOAP based web services in the WCF.
My client sending the request to the WCF services with the following SOAP request xml:
<e:Envelope xmlns:e="http://schemas.xmlsoap.org/soap/envelope/" e:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<e:Header>
<me:RequestHead xmlns:me="http://www.my-namespace.org/header/abc" >
<ID>0</ID>
<Green>
<a>101</a>
<b>0</b>
</Green>
<Time>1</Tim>
</me:RequestHead>
</e:Header>
<e:Body>
<m:RequestBody xmlns:m="http://www.my-namespace.org/service/abc">
<Time>
<hh>23</hh>
<mm>59</mm>
<ss>59</ss>
</Time>
</m:RequestBody>
</e:Body>
</e:Envelope>
I created the MessageContract class to pass the value to client or get the value from the client.
In MessageContract class there is multiple MessageHeader attribute property, but when request send to the server the property with MessageHeader attribute is not deserialize and it is always null.
In RequestHead tag there is prefix me, I think its create the problem to deserialize the XML request to the object at the WCF services
But in RequestBody tag there is also prefix m but it deserialize successfully.
When I remove the prefix me from the RequestHead tag than it worked successfully. and I got the value of the MessageHeader attribute property.
<e:Envelope xmlns:e="http://schemas.xmlsoap.org/soap/envelope/" e:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<e:Header>
<RequestHead xmlns="http://www.my-namespace.org/header/abc" >
<ID>0</ID>
<Green>
<a>101</a>
<b>0</b>
</Green>
<Time>1</Tim>
</RequestHead>
</e:Header>
<e:Body>
<m:RequestBody xmlns:m="http://www.my-namespace.org/service/abc">
<hh>23</hh>
<mm>59</mm>
<ss>59</ss>
</m:RequestBody>
</e:Body>
</e:Envelope>
My Classes:
[MessageContract]
class Green
{
[MessageHeader(Namespace = "")]
public string a { get; set; }
[MessageHeader(Namespace = "")]
public string b { get; set; }
}
[MessageContract]
class RequestHead
{
[MessageHeader(Namespace = "")]
public int id { get; set; }
[MessageHeader(Namespace = "")]
public Green green { get; set; }
[MessageHeader(Namespace = "")]
public int Time { get; set; }
}
[MessageContract]
class Head
{
[MessageHeader(Namespace = "http://www.my-namespace.org/header/abc")]
public RequestHead RequestHead { get; set; }
}
[MessageContract(Namespace = "http://www.my-namespace.org/service/abc")]
class RequestBody : Head
{
[MessageBodyMember(Order = 1)]
public int hh { get; set; }
[MessageBodyMember(Order = 1)]
public int mm { get; set; }
[MessageBodyMember(Order = 1)]
public int ss { get; set; }
}
And my action method in the WCF services is look like this:
public RequestBody CheckTime(RequestBody objBody)
{
}
When I checked the request SOAP XML using the following code:
OperationContext.Current.RequestContext.RequestMessage.ToString();
It give me the full request SOAP XML with the header value also but does not deserialize it to the object.
I does not understand where I am going wrong or I am missing something.
Here is the same question asked but till now it is does not get any answered: How can I deserialize a custom SOAP header in WCF?
Thanks for help...

Get null by deserialize in C# from XML if string between tags

i need some help, i get allways null if i will deserialize a string between two xml tags.
The following example xml file:
<?xml version="1.0" encoding="utf-8" ?>
<item name='First Item' size='1'>
<Bits value='0'>
1st String
</Bits>
<Bits value='1'>
2nd String
</Bits>
</item>
And i write the following classes:
[Serializable()]
public class Bits
{
[XmlElement(ElementName = "Bits")]
public String entryString { get; set; }
[XmlAttribute("value")]
public int entryValue { get; set; }
}
[Serializable()]
[XmlRoot("item")]
public class itemsReader
{
public itemsReader()
{
_bitList = new List<Bits>();
}
[XmlElement("Bits")]
public List<Bits> _bitList { get; set; }
[XmlAttribute("name")]
public String entryName { get; set; }
[XmlAttribute("size")]
public int entrySize { get; set; }
}
Only the entryString is always null!
To readout the XML file i use the following:
itemsReader ireader = null;
String path = #"PathtoString";
XmlSerializer serializer = new XmlSerializer(typeof(itemsReader));
var reader = File.OpenText(path);
ireader = (itemsReader)serializer.Deserialize(reader);
Thanks for help!
You have decorated entryString with XmlElement attribute which will search for an element Bits inside Bits itself.
But you want inner text of Bits element. For that we have another attribute called XmlText.
So change this
[XmlElement(ElementName = "Bits")]
public String entryString { get; set; }
to
[XmlText]
public String entryString { get; set; }
i ran your code on your input and got the output, but the i saw, in the Bits class, that the property entryString has an attribute
[XmlElement(ElementName = "Bits")]
but in your xml file there is no <Bits> tags around 1st String. so i added them and got:
<?xml version="1.0"?>
<item name="First Item" size="2">
<Bits value="1">
<Bits>firstOne</Bits>
</Bits>
<Bits value="2">
<Bits>secOne</Bits>
</Bits>
</item>
and then your code worked
so you can change the xml file or get rid of the attribute. your call

Deserialization Error: The XML element 'name' from namespace '' is already present in the current scope

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.example.com/1.0/ http://api.example.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.example.com/album.php?aid=7333&id=1337262814</link>
<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.example.com/album.php?aid=3296&id=1337262814</link>
<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.example.com/album.php?aid=6300&id=1337262814</link>
<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.example.com/album.php?aid=5234&id=1337262814</link>
<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.example.com/album.php?aid=4937&id=1637262814</link>
<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.example.com/album.php?aid=4547&id=1637262814</link>
<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.example.com/album.php?aid=4493&id=1637262814</link>
<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.
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.example.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://api.example.com/1.0/ http://api.example.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.example.com/album.php?aid=7733&id=1124262814</link>
<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.example.com/album.php?aid=5296&id=1124262814</link>
<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.example.com/album.php?aid=6600&id=1124262814</link>
<size>3</size>
<visible>friends</visible>
</album>
</photos_GetAlbums_response>
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.
Here we go... note the xml was invalid (& should be &; 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.example.com/1.0/ http://api.example.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.example.com/album.php?aid=7333&id=1337262814</link>
<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.example.com/album.php?aid=3296&id=1337262814</link>
<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.example.com/album.php?aid=6300&id=1337262814</link>
<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.example.com/album.php?aid=5234&id=1337262814</link>
<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.example.com/album.php?aid=4937&id=1637262814</link>
<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.example.com/album.php?aid=4547&id=1637262814</link>
<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.example.com/album.php?aid=4493&id=1637262814</link>
<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; }
}
(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 about it:
[Serializable, XmlRoot("photos_GetAlbums_response",
Namespace="http://api.example.com/1.0/")]
public class GetAlbumsResponse { /* code as before */ }
[Serializable, XmlType(Namespace="http://api.example.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 & (not &). Suggest some "Replace"... at the crudest level:
string fixedXml = xml.Replace("&", "&");
(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".
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...
}
Well,
there is a link from Microsoft targeting your problem
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 ?
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.
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);
This is a really old thread but I just faced the same issue my self while tried to serialize two different list types in the same class under the same XmlArray name, something like
<Root>
<ArrayNode>
<SubnodeType1>...</SubnodeType1>
<SubnodeType1>...</SubnodeType1>
</ArrayNode>
</Root>
Or
<Root>
<ArrayNode>
<SubnodeType2>...</SubnodeType2>
<SubnodeType2>...</SubnodeType2>
</ArrayNode>
</Root>
What has worked for me was to use a class decoration like:
[XmlRoot(Namespace = "", ElementName = "Root")]
public class Root
{
[XmlArray(ElementName = "ArrayNode", Namespace = "", IsNullable = false, Order = 1)]
[XmlArrayItem("SubnodeType1")]
public List<SubnodeType1> SubnodeType1 { get; set; }
[XmlArray(ElementName = "ArrayNode", Namespace = "", IsNullable = false, Order = 2)]
[XmlArrayItem("SubnodeType2")]
public List<SubnodeType2> SubnodeType2 { get; set; }
}
Hope it help someone else :)

Categories