Json Serialization for Windows Phone - c#

I was trying to implement parsing a JSON response as shown here for my Windows Phone 7 project in C#. But I am stuck with a compilation error as "The type or namespace name 'Serializable' could not be found (are you missing a using directive or an assembly reference?)"
I have the imports using System.Runtime.Serialization;
using System.Runtime.Serialization.Json; I am not sure what are import I am missing. I tried to include using System.ServiceModel.Web; But the Web part is not recognized.
I thought my project is not pointing to the right framework from here. But in the Assembly information, there is no option for me to change the target framework.
This looks like a similar problem to mine, but I couldn't find the JSON.NET in .net dlls which is filtered for Windows Phone.
Can someone help me to get this JSON thing working for Windows Phone 7.
Thank in Advance.
EDIT - 7/3/11
My Jason response is
{ "serviceresponse" : { "servicename" : "RequestRegisterUser", .....
And my Response objects are:
[DataContract]
public class serviceresponse
{
[DataMember]
public String servicename { get; set; }
.
.
.
And my Deserialize method:
public static T Deserialise<T>(string json)
{
T obj = Activator.CreateInstance<T>();
using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
obj = (T)serializer.ReadObject(stream);
return obj;
}
}
Now I am getting this error after Deserializing the response:
servicename Could not evaluate expression string
( I could not import System.ServiceModel.Web though I have the dll in the reference. A compilation error on the .Web part (The type or namespace name 'Web' does not exist in the namespace 'System.ServiceModel') )
EDIT After more research, I found my response when viewed in the debugger is actually
{
\"serviceresponse\": {
\"servicename\": \"RequestRegisterUser\",.....
I searched for this and found this could be a problem. How can I format it to correct JSON String??

You need to add a reference to both System.Runtime.Serialization and System.ServiceModel.Web assemblies. The DataContractJsonSerializer is defined in System.ServiceModel.Web assembly in the Silverlight version of the framework, that's why you need the extra assembly reference.
And by the way JSON.NET is a a popular open-source JSON framework for .Net and you could find more about it here. It's not part of the .Net framework, that's why you can't find it.
Edit:
About the compilation, in Silverlight the DataContractJsonSerializer is in the System.Runtime.Serialization.Json namespace, but in the assembly System.ServiceModel.Web (in System.ServiceModel.Web.dll), which is a bit confusing. So you use it like this - System.Runtime.Serialization.Json.DataContractJsonSerializer, but need the extra assembly reference. You also need to reference the System.Runtime.Serialization assembly as well, because that is where the DataContract attribute is defined. I see you have already successfully compiled the code, but I hope the extra explanation makes it more clear for future readers.
About the serialization itself - as you have already found out, you will need two objects, simply because that's the structure of the json. However, the DataContract and DataMember attributes have a Name property that you can use instead of changing the name of the fields. Also, you can use properties instead of fields if you like.
For example:
[DataContract]
public class ServiceResponse
{
[DataMember(Name = "servicename")]
public string ServiceName { get; set; }
}
[DataContract]
class Response
{
[DataMember(Name = "serviceresponse")]
public ServiceResponse ServiceResponse { get; set; }
}
And one last thing - you don't need the call to Activator.CreateInstance(); in your Deserialise method.

It certainly would help if you posted your code. So I can only guess:
I assume you have something like this:
[Serializable]
public class Response
{
[DataMember]
public string name { get; set; }
...
}
But that's a mix-up of two serialization concepts, one of which is not supported in Phone 7. The correct attributes are DataContract and DataMember:
[DataContract]
public class Response
{
[DataMember]
public string name { get; set; }
...
}

I found the issue. Though my class name is "serviceresponse", I used another wrapper class as
public class Response
{
public serviceresponse serviceres;//Don't Do this....
}
where I used the variable name for serviceresponse as serviceres. But when I changed it to " serviceresponse" its all working.
public class Response
{
public serviceresponse serviceresponse;//This fixed it.
}

Related

DataContractSerializer returns null for all the variables when I try to deserialize

I have a problem where DataContractSerializer returns null for all my variable. It's like it doesn't see them or something. I'm using it to deserialize a json file into an object. I had it working for another json file that was using another class with 3 string attributes. This one is composed of 40 attributes mostly string and a few bool. I have been working on it for hours and I just can't seem to find what I'm doing wrong. I even tried it with only 1 string attribute and it still returned null. Here is a simplified version with only 1 string attribute and 1 bool attribute. Any advice is more than appreciated.
Thank you
Json :
[{"Proposal_x0020_Type":"Lite Proposal","BI_x0020_Criteria_x0020_1":true}]
Function that tries to deserialize the string:
public Proposal[] Deserializer(string jsonFile)
{
MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonFile));
DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(Proposal[]));
Proposal[] projectArr = (Proposal[])deserializer.ReadObject(ms);
Console.WriteLine(jsonFile);
Console.ReadLine();
return projectArr;
}
Class of the object that the deserializer should create:
namespace PMIS
{
[DataContract]
public class Proposal
{
[DataMember(Order = 0)]
public string Proposal_x0020_Type { get; set; }
[DataMember(Order = 1)]
public bool BI_x0020_Criteria_x0020_1 { get; set; }
}
}
I managed to find the problem by comparing the code of this Deserialization to a previous one that I had. The only difference was the name of the variables inside the Json file. The fact that they contain the Unicode for space (x00200) causes some problem inside the DataContractDeserializer. I believe that it is seeing it as space, so instead of seeing "Proposal_x0020_Type", it is seeing "Proposal_ _Type", but I'm not sure about it. Anyway, the solution was to remove x0020 from all the variables inside the Json file. After that it worked perfectly fine.

Conditional JSON Deserialization using C#

I'm writing a C# test automation to validate web services that return JSON strings. I created a DataContract that mapped to what was being returned. Assume this is what was being returned:
{"DataModule" : {"required":"false", "location":"toolbar"}}
My test automation was working fine, but then I started getting this error:
"The data contract type 'DataModule' cannot be deserialized because
the required data members 'required, location' were not found."
I checked the JSON and the data module was now
{"DataModule" : {"width":"400", "height":"320"}}
I discovered that the dev implementation is that if the first type of data module is encountered, the client parses that and creates a button on a toolbar. If the second type of data module is returned, the button appears on the toolbar AND a panel appears in another location with those measurements.
Is there a way to either create optional members in a data contract OR implement conditional deserialization to account for JSON objects that may have multiple implementations?
If you declare the model with all of the likely properties, only the ones found in the JSON string will be populated:
public class DataModule
{
public bool Required { get; set; }
public string Location { get; set; }
public string Width { get; set; }
public string Height { get; set; }
}
#dave-s
I had already tried adding all of the properties, but since I knew I was on the right track, your suggestion keyed me into something else. The properties were all decorated with
[System.Runtime.Serialization.DataMemberAttribute(IsRequired = false)]
But the class itself, was decorated with only [Serializable]. When I changed [Serializable] to
[System.Runtime.Serialization.DataContractAttribute()]
it started working.

How to set up an application to use DataContracts?

I'm developing a desktop application with .NET. I'd like to save some data into a file in a way that would later give me some degree of freedom in changing the data I'm saving, such as adding new fields, while retaining the possibility to read saves from older formats.
This answer recommends to use DataContractSerializer instead of BinaryFormatter.
However I can't use the [DataContract] attribute on my classes in the project. After using System.Runtime.Serialization; I still get errors about unknown types.
The project targets .NET Framework 4.
I've learned that Data Contracts are part of the WCF framework, I assume I should somehow configure my project to use it. How?
In C# namespaces can be shared across multiple assemblies. You have to add a reference to System.Runtime.Serialization.dll, which contains [DataContract] attribute.
probably you are missing to specify the Know Type attribute when it is needed
Have a look at the below example:
public interface ICustomerInfo
{
string ReturnCustomerName();
}
[DataContract(Name = "Customer")]
public class CustomerTypeA : ICustomerInfo
{
public string ReturnCustomerName()
{
return "no name";
}
}
[DataContract(Name = "Customer")]
public class CustomerTypeB : ICustomerInfo
{
public string ReturnCustomerName()
{
return "no name";
}
}
[DataContract]
[KnownType(typeof(CustomerTypeB))]
public class PurchaseOrder
{
[DataMember]
ICustomerInfo buyer;
[DataMember]
int amount;
}
you have to specify the type of ICustomerInfo otherwise the serialization engine cannot guess the type
Just add wcf service template to your application and declare your function and data members their and reference wcf in your project.

InvalidDataContractException on WP7 application on class that is serializable

I'm having an strange error when trying to save an object into isolated storage. I have a class that has some properties, here's the code :
[DataContract]
public class ExerciseStatistic
{
[XmlIgnore]
public int CorrectAnswers
{
get
{
return Attempts.Where(a => a.AttemptAnswerIsCorrect).Count();
}
}
[XmlIgnore]
public int IncorrectAnswers
{
get
{
return Attempts.Where(a => !a.AttemptAnswerIsCorrect).Count();
}
}
[XmlIgnore]
public int AnswerAttempts
{
get { return Attempts.Count; }
}
public List<AnswerAttempt> Attempts { get; set; }
public ExerciseStatistic()
{
Attempts = new List<AnswerAttempt>();
}
}
public class AnswerAttempt
{
public DateTime AttemptDate { get; set; }
public string AttemptTargetName { get; set; }
public string AttemptName { get; set; }
public bool AttemptAnswerIsCorrect { get; set; }
}
However, when trying to save it with this sentence :
IsolatedStorageSettings.ApplicationSettings["a"] = new ExerciseStatistic()
{
Attempts = new List<AnswerAttempt>()
{
new AnswerAttempt()
{
AttemptAnswerIsCorrect = true,
AttemptDate = DateTime.Now,
AttemptName = "lala",
AttemptTargetName = "lala2"
},
new AnswerAttempt()
{
AttemptAnswerIsCorrect = false,
AttemptDate = DateTime.Now,
AttemptName = "lalab",
AttemptTargetName = "lalab2"
}
}
};
I'm getting an exception like this one (i changed a bit the signature of the code with fake names, but for the example it serves its purpose) :
Type 'XX.Model.FirstClass.SecondClass' cannot be serialized. Consider
marking it with the DataContractAttribute attribute, and marking all
of its members you want serialized with the DataMemberAttribute
attribute.
I don't understand why the serializer is trying to serialize an object of my model (which is not serializable) when the class that I'm giving it doesn't have any references to that kind of type... what am i missing? -> nope, i don't want to add datacontract attributes to classes that i don't need and am not planning to serialize, so please don't answer with this :)
You might experience this problem if you work through the reference procedure in "Walkthrough: Consuming OData with MVVM for Windows Phone" at http://msdn.microsoft.com/en-us/library/hh394007(v=VS.92).aspx
When you get to the point where you call :
Return DataServiceState.Serialize(_context, collections);
You might get an InvalidDataContractException with the message:
Type 'DataBoundApp1.Northwind.NorthwindEntities' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.
Thanks to the answer by Daniel Perez, I was able to resolve this problem and I am documenting my steps to clarify the solution for others:
Show hidden files in Solution Explorer
Open the file "Reference.cs" (under your Service Reference, expand
Reference.datasvcmap)
If your Data Service Context class is missing the [DataContract]
attribute, add it as shown here:
.
namespace OCC.WindowsPhone.OrlandoCodeCampService
{
[DataContract] <--- I ADDED THIS
public partial class OrlandoCodeCampEntities : global::System.Data.Services.Client.DataServiceContext
{..}
Once I added the DataContract attribute, the problem went away!
It seems to me you try to exclude properties from serialization by using XmlIgnore.
From the documentation:
You can opt out members from serialization by using the IgnoreDataMemberAttribute.
so try using IgnoreDataMemberAttribute instead of XmlIgnore to opt out members from serialization.
I also had some troubles with DataContract in the very same situation as you, therefore I reverted to plain old XML serialization to strings, which i then stored in isolated storage. This also eases debugging.
I don't think that this is a proper answer, but it's what i had to do in order to fix it.
After changing some more the code, i realised that this was failing EVEN if I wasn't saving anything to the isolated storage. Just declaring a DataContract attribute on the type made the error arise. I must think that WP7's framework at some point is parsing all classes that have this attribute, and for some strange and obscure reason (which i can't find) it's looking for them in other classes as well. I added the DataContract attributes in the classes that the framework is complaning about, and also some KnownType attributes as well, and everything started to run smoothly... weird weird... if someone can shed some light into this, i'd be happy (i hate it when i solve a problem but without knowing the exact cause)

Using a generic object to return data from a webservice method?

I have an ASMX webservice with a number of methods which will return XML.
The service returns various different objects and I have created a wrapper object which contains information about the request e.g:
[Serializable]
[XmlRoot("response")]
public class DtoWrapper<T>
{
[XmlElement("error")]
public bool Error { get; set; }
[XmlElement("error_message")]
public string ErrorMessage { get; set; }
[XmlElement("success")]
public bool Success { get; set; }
[XmlElement("friendly_message")]
public string FriendlyMessage { get; set; }
[XmlArray("result")]
[XmlArrayItem("item")]
public List<T> Payload { get; set; }
}
Now this works fine until I defined my second method with a different type. Then I get this error when I try and load the ASMX test page
The top XML element 'response' from namespace 'http://tempuri.org/'
references distinct types
MyProject.Web.webservices.DtoWrapper1[MyProject.BusinessLogic.ClassA]
and
MyProject.Web.webservices.DtoWrapper1[MyProject.BusinessLogic.ClassB].
Use XML attributes to specify another
XML name or namespace for the element
or types.
I have tried marking my objects up with [XmlType(Namespace="com.temp.A")] and [XmlType(Namespace="com.temp.B")] but it doesn't seem to help.
Any ideas? Will I have to create a wrapper object for each type I want to use?
EDIT: I've realised it's not actually the type arguments that are the problem. It's the fact that the [XmlRoot] tag is specified on the class. The serializer is treating them as 2 types but they have the same root element in the same namespace.
You cannot do this. XML has no concept of generics, neither do XML Schema or SOAP. As far as XML Schema is concerned, if it has the same element name and same namespace, then it's the same thing.
You cannot have a generic web service, as the concepts do not exist.

Categories

Resources