Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize <ArrayOf> in XML to List<>

I have trouble deserializing the result from my WCF webservice. The method returns a List<RecipeEntity>, which is serialized to XML as shown below. When I try to deserialize I just get an exception, shown below. It seems I cannot deserialize <ArrayOfRecipe> to List<RecipeEntity>. Note that RecipeEntity is mapped by contract name to Recipe.

After searching I see many propose XmlArray and XmlElement attributes, but as far as I can tell they do not apply here on the GetRecipes() method. I have only seen them used on fields of serialized classes.

I know I could wrap the List<RecipeEntity> in a RecipeList class and return that instead, but I would rather deserialize directly to List<> for any given type.

Exception:

System.InvalidOperationException was caught
  Message=There is an error in XML document (1, 2).
  StackTrace:
       at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, Object events)
       at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)
       at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader)
       at GroceriesAppSL.Pages.Home.GetRecipesCallback(RestResponse response)
  InnerException: System.InvalidOperationException
       Message=<ArrayOfRecipe xmlns='Groceries.Entities'> was not expected.
       StackTrace:
            at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderList1.Read5_Recipe()
       InnerException: 

Data contract:

[DataContract(Name = "Recipe", Namespace = "Groceries.Entities")]
public class RecipeEntity
{
    [DataMember] public int Id;
    [DataMember] public string Name;
    [DataMember] public string Description;
}

Implementation:

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    [WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = "Recipes/{username}")]
    List<RecipeEntity> GetRecipes(string username);
}

public class MyService : IMyService
{
    public List<RecipeEntity> GetRecipes(string username)
    {
        return _recipeDB.Recipes.Select(ToEntity).ToList();
    }
}

Example XML result, for illustration purposes only.

<ArrayOfRecipe xmlns="Groceries.Entities" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Recipe>
<Id>139</Id>
<Name>ExampleRecipe</Name>
<Description>5 L milk;4 eggs</Description>
</Recipe>
<Recipe>...</Recipe>
<Recipe>...</Recipe>
<Recipe>...</Recipe>
...
</ArrayOfRecipe>

Deserialization code:

using (var xmlReader = XmlReader.Create(new StringReader(response.Content)))
{
    var xs = new System.Xml.Serialization.XmlSerializer(typeof(List<RecipeEntity>));
    var recipes = (List<RecipeEntity>)xs.Deserialize(xmlReader);
}
like image 857
angularsen Avatar asked Apr 24 '11 21:04

angularsen


2 Answers

You are using DataContractSerializer to serialize and XmlSerializer to deserialize. Those two doesn't use the same approach. You must either use DataContractSerializer in your deserialization method or you must mark your operation or service with XmlSerializerFormat attribute (in such case WCF will use XmlSerializer instead of DataContractSerializer). DataContract and DataMember attributes are only for DataContractSerializer. XmlSerializer uses its own attributes defined in System.Xml.Serialization namespace.

like image 93
Ladislav Mrnka Avatar answered Oct 18 '22 08:10

Ladislav Mrnka


Simply first you get the response stream and then use the DataContractSerealizer to deserealize it.

DeSerealization code:

using(Stream answer=webResponse.GetResponseStream())
    {
     DataContractSerializer xmlSer = new DataContractSerializer(typeof(RecipeEntity[]));
     var RecipeList = (RecipeEntity[])xmlSer.ReadObject(answer);
    }

c#

like image 37
sarawgeek Avatar answered Oct 18 '22 06:10

sarawgeek