I have a generic serializer and deserializer for messages that are sent over a network connection:
public static async Task<string> SerializeObject<T>(Object obj)
{
string objectStr;
using (var memStream = new MemoryStream())
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(memStream, (T)obj);
memStream.Position = 0;
using (var sr = new StreamReader(memStream))
{
objectStr = await sr.ReadToEndAsync();
}
}
return objectStr;
}
public static async Task<T> DeserializeObject<T>(string obj)
{
using (var memStream = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(obj);
memStream.Write(data, 0, data.Length);
memStream.Position = 0;
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
T result = (T)(serializer.ReadObject(memStream));
return result;
}
}
and I have a simple class that I serialize and deserialize. This is one of many classes like this, that's why I have the generic (de)serializer:
[DataContract]
public class SignatureMessage : NetworkMessage
{
[DataMember]
public string AppName { get; set; }
[DataMember]
public string AppUserName { get; set; }
[DataMember]
public string AppUserID { get; set; }
[DataMember]
public string IPAdress { get; set; }
[DataMember]
public string Port { get; set; }
}
When I set my project to 'Release' instead of 'Debug'. I get an XMLException EncounteredWithNameNamespaceExpectingElement. After research online, the suggestion was made to change my class to have an empty namespace in the datacontract element like this:
[DataContract(Namespace = "")]
public class SignatureMessage : NetworkMessage
{
[DataMember]
public string AppName { get; set; }
[DataMember]
public string AppUserName { get; set; }
[DataMember]
public string AppUserID { get; set; }
[DataMember]
public string IPAdress { get; set; }
[DataMember]
public string Port { get; set; }
}
When I tried this I got a different XMLException XmlPrefixBoundToNamespace. How can I apply the proper namespacing so both serializing and deserializing will work?
At first there were two things which catched my eyes:
You pass an Object to your SerializeObject method instead of the actual type T.
The empty namespace. This empty namespace tip is given in an MSDN article for visual basic. But still I'd ignore this tip and set the namespace to something unique and meaningful. Just remember: the namespace has to be the same on the client and the service for the same type.
If these two things won't help your cause you could possibly be running into a KnownType-Issue. Therefore I'd also recommend reading this article and utilizing the GenericResolver used in it.
Edit: Although my tips might not help you with your problem they are still true. But I noticed something in the full exception that you posted. http://schemas.datacontract.org/2004/07/ is the default namespace for a DataContract if none was specified explicitly. It could mean that you are trying to send something from Listen.Extensions. So this may also be a good starting point to look for possible errors.
I tried to reduce the problem. I put all code in one console application, removed the async and await keywords, removed the NetworkMessage inheritance and used public static async Task<string> SerializeObject<T>(T obj). No problems in debug and release mode. So look at the NetworkMessage implementation (not specified, not a .Net class I know) and be sure to use the same dll (with SignatureMessage and the other classes) on client and server.
Related
I'm trying to deserialize json data with DataContractJsonSerializer class. a problem is how to set root element?
my json data is here.
{
"delete":{
"status":{
"id":696142765093072896,
"id_str":"696142765093072896",
"user_id":2223183576,
"user_id_str":"2223183576"
},
"timestamp_ms":"1454808363540"
}
}
and I wrote class for deserialization like this. but it isn't works. my Status always null.
[DataContract(Name="delete")]
public class Delete
{
[DataMember(Name="status")]
public DeletedStatus Status { get; set; }
}
public class DeletedStatus
{
[DataMember(Name = "id")]
public long Id { get; set; }
[DataMember(Name = "user_id")]
public long UserId { get; set; }
}
how can I start parse json from specific element?
Based on what I can tell from the JSON, the deserialization appears to be failing because the root property of the object is the "delete" property. I don't believe this will work with the DataContractJsonSerializer simply because the given type will not match the Delete type. One other possible issue is that I see the DeleteStatus class is missing a [DataContract] attribute.
Long story short, there is no simple way of doing what you want to do. That being said, there is a short and sweet way of deserializing the JSON without adding a lot of extra headache. I suggest creating a data type that represents the JSON in its current state, and deserialize to that type instead.
I wrote a Unit Test that you can run from a Visual Studio test project. I hope this helps.
JsonDeserializationTests.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Runtime.Serialization.Json;
using System.IO;
namespace SerializationTests {
[TestClass]
public class JsonDeserializationTests {
[TestMethod]
public void Deserialize_Delete_Type_Success() {
string json = string.Empty;
//Set the DataContractJsonSerializer target type to our wrapper type.
var ser = new DataContractJsonSerializer(typeof(DeleteWrapperJsonResult));
//Create an instance of the wrapper that reflects the JSON that you gave.
//This will help me mock the data that you gave.
var deleteWrapper = new DeleteWrapperJsonResult {
delete = new DeleteJsonResult {
status = new DeletedStatusJsonResult {
id = 696142765093072896,
user_id = 2223183576
}
}
};
//Convert the mock data to JSON to reflect the JSON that you gave.
using (var serStream = new MemoryStream()) {
using (var sr = new StreamReader(serStream)) {
ser.WriteObject(serStream, deleteWrapper);
serStream.Position = 0;
json = sr.ReadToEnd(); //Set the JSON string here.
//Output "{\"delete\":{\"status\":{\"id\":696142765093072896,\"id_str\":\"696142765093072896\",\"user_id\":2223183576,\"user_id_str\":\"2223183576\"}}}"
}
}
//Prepeare to Deserialize the JSON.
var deserialized = default(DeleteWrapperJsonResult);
using (var deserStream = new MemoryStream()) {
using (var sw = new StreamWriter(deserStream)) {
sw.Write(json); //Write the JSON to the MemoryStream
sw.Flush();
deserStream.Seek(0, SeekOrigin.Begin);
//Deserialize the JSON into an instance of our wrapper class.
//This works because of the structure of the JSON.
deserialized = (DeleteWrapperJsonResult)ser.ReadObject(deserStream);
}
}
//Initialize the actual Delete instanace with what was deserialized.
var delete = new Delete {
Status = new DeletedStatus {
//These values were populated with the JSON values.
UserId = deserialized.delete.status.user_id,
Id = deserialized.delete.status.id
}
};
//Write asserts around what was given and check for equality.
Assert.AreEqual(delete.Status.UserId, deleteWrapper.delete.status.user_id);
Assert.AreEqual(delete.Status.Id, deleteWrapper.delete.status.id);
//Test Passes for Me
}
}
}
Delete.cs
using System.Runtime.Serialization;
namespace SerializationTests {
[DataContract]
[KnownType(typeof(Delete))]
public class Delete {
[DataMember]
public DeletedStatus Status { get; set; }
}
[DataContract]
[KnownType(typeof(DeletedStatus))]
public class DeletedStatus {
[DataMember]
public long Id { get; set; }
[DataMember]
public long UserId { get; set; }
}
/**************************************************************
These types below are what comprise our wrapper class so that we can
use the JSON in its current state. The wrapper classes have properties that
are synonymous with the JSON properties.
**************************************************************/
//This structure represents the object nesting as it appears currently in your example.
[DataContract]
[KnownType(typeof(DeleteJsonResult))]
public class DeleteWrapperJsonResult {
[DataMember]
public DeleteJsonResult delete { get; set; }
}
[DataContract]
[KnownType(typeof(DeleteJsonResult))]
public class DeleteJsonResult {
[DataMember]
public DeletedStatusJsonResult status { get; set; }
}
[DataContract]
[KnownType(typeof(DeletedStatusJsonResult))]
public class DeletedStatusJsonResult {
[DataMember]
public long id { get; set; }
[DataMember]
public string id_str {
get {
return id.ToString();
}
set {
return;
}
}
[DataMember]
public long user_id { get; set; }
[DataMember]
public string user_id_str {
get {
return user_id.ToString();
}
set {
return;
}
}
}
}
As of the time of this writing, my unit test is passing! Let me know if I can assist further.
I have classes that look like this, based on the json being returned by Slack's api:
public class Response<T>
{
public bool ok { get; set; }
public string error { get; set; }
}
public class PostMessage : Response<PostMessage>
{
public string ts { get; set; }
public string channel { get; set; }
public Message message { get; set; }
}
public class ChannelsHistory : Response<ChannelsHistory>
{
public string latest { get; set; }
public List<Message> messages { get; set; }
public bool has_more { get; set; }
}
And I want to write a single method that can bottleneck the call to JsonConvert.DeserializeObject<T>. I don't know too much about the details of the implementation behind that method, but I thought that this would work:
internal static Response<T> GetSlackResponse<T>(List<KeyValuePair<string, string>> parameters = null)
{
Uri slackUri = BuidSlackUri(typeof(T), parameters);
String jsonResponse = GetJson(slackUri);
Response<T> response = JsonConvert.DeserializeObject<Response<T>>(jsonResponse);
if (!response.ok)
{
Aesthetic.Catch("The Slack API failed to respond successfully. " + response.error);
}
return response;
}
Nothing is failing, but not all of the properties I need are being deserialized. For example, a call to GetSlackResponse() will return a Response that has ok set to true, but I won't have access to the Message property of the PostMessage class. I've tried casting (both explicitly and with as), to no avail.
I'm sure I'm missing something simple here, can someone point it out?
Having my various Response class extend a generically typed class was a red herring; it introduced nothing but making the problem more confusing. I only needed the method to be generic, not the type itself.
Changing the relevant line in GetSlackResponse<T>() method from
Response<T> response = JsonConvert.DeserializeObject<Response<T>>(jsonResponse);
to
T response = JsonConvert.DeserializeObject<T>(jsonResponse);
fixed everything. Now my various response types only need to extend my base Response class, and will be properly deserialized by JSON.NET. There is no need for the response to be a generically typed object.
I have a Class Library project with something like this....
[DataContract]
public class DomainVerify
{
[DataMember]
internal string key { get; set; }
public string domain { get; set; }
public string mailbox { get; set; }
}
I made key Internal, so my other projects can't see this property. Now problem is, within the same project where I have "DomainVerify" Class, I am using JavaScriptSerialize to Serialize my Class into a JSON string & it seems like I can't serialze the "key" property since its "internal".
var json = new JavaScriptSerializer().Serialize(DomainVerify); // Skips Key property
is there anyway so I can hide this property to other projects and yet can serialize this property within same project?
I think you have to use DataContractJsonSerializer.
Following is the example provided on MSDN. This example is with internal property.
http://msdn.microsoft.com/en-us/library/bb412179(v=vs.110).aspx.
I did not tried with JavaScriptSerializer but if there is some setting that allow then only it is possible to serialize internal so I suggest tried with DataContractJsonSerialier.
If this not solve your problem then look for Newton.Json.
http://www.tecsupra.com/serializing-only-some-properties-of-an-object-to-json-using-newtonsoft-json-net/
Last but not least make sure that your internal property has some value.
DataContractJsonSerializer
class Program
{
static void Main(string[] args)
{
DomainVerify verify = new DomainVerify() { domain = "test", key = "myKey", mailbox = "Newkey" };
Console.WriteLine(GetJsonString(verify));
Console.ReadLine();
}
public static string GetJsonString(object o)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(o.GetType());
using (MemoryStream ms = new MemoryStream())
{
ser.WriteObject(ms, o);
string jsonData = Encoding.Default.GetString(ms.ToArray());
return jsonData;
}
}
}
[DataContract]
public class DomainVerify
{
[DataMember]
internal string key { get; set; }
[DataMember]
public string domain { get; set; }
[DataMember]
public string mailbox { get; set; }
}
In case of WCF you have to mark each property that you want to take part in serialization using datacontract.
you have to add System.ServiceModel and System.Runtime.Serialization assembly.
In case of Json.net ( you have to add package)
public class DomainVerify
{
[Newtonsoft.Json.JsonProperty] // if you don't mark this attribute then as this is internal property it will not serilize.
internal string key { get; set; }
[Newtonsoft.Json.JsonProperty] // as this is public property it always include in serilization unless you mark it with JsonIgnore.
public string domain { get; set; }
[Newtonsoft.Json.JsonProperty]
public string mailbox { get; set; }
}
class Program
{
static void Main(string[] args)
{
DomainVerify verify = new DomainVerify() { domain = "test", key = "myKey", mailbox = "Newkey" };
Console.WriteLine(GetJsonString(verify));
Console.ReadLine();
}
public static string GetJsonString(object o)
{
return JsonConvert.SerializeObject(o);
}
}
I hope above thing help you.
Note: I still have some doubt that if you pass this object from other project then it may possible that this field does not contain proper value or empty because it is internal.
This is the json string I need to deserialize:
[{"id":5236083584722820,"name":"IT_Projects","accessLevel":"EDITOR"},
{"id":2034305724639108,"name":"IT_Task","accessLevel":"EDITOR"},
{"id":2249810003683204,"name":"On-Hold","accessLevel":"EDITOR"}]
Here is the code:
[DataContract]
public class SSCollection
{
[DataMember]
public List<SSheets> sheetObjects { get; set; }
}
[DataContract]
public class SSheets
{
[DataMember]
public Int64 id { get; set; }
[DataMember]
public string name { get; set; }
[DataMember]
public string accessLevel { get; set;}
}
using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
memoryStream.Position = 0;
var serializer = new DataContractJsonSerializer(typeof(SSCollection));
SSCollection ss = (SSCollection)serializer.ReadObject(memoryStream);
return ss;
}
I'm not sure how to handle the json string, it appears to be an array but is not named. I've tried with using the SSheets DataContract and with using SSCollection, both return nulls except when I use SSheets i get a 0 for id and nulls for the strings name and accessLevel. Any help will be greatly appreciated. thanks.
AFter reading many other questions on this subject and reading their sugggestions to use Json.Net I have decided to use Newtonsoft.Json. It worked on the first try. .Net DataContract just not worth the hassle. Thanks for your help.
I am trying to deserialize a json response from a google api, so i thought i would define a couple classes to help with it:
[DataContract]
public class DetectionResult:ResponseData
{
[DataMember(Name="language")]
public string Language
{ get; set; }
[DataMember(Name="isReliable")]
public bool IsReliable
{ get; set; }
[DataMember(Name="confidence")]
public double Confidence
{get;set;}
}
[DataContract]
public abstract class ResponseData
{
[DataMember(Name = "error")]
public TranslationError Error
{ get; set; }
}
[DataContract]
public class TranslationError
{
[DataMember(Name="code")]
public int Code
{ get; set; }
[DataMember(Name="message" )]
public int Message
{ get; set; }
}
[DataContract]
[KnownType(typeof(DetectionResult))]
public class RequestResult
{
[DataMember(Name="responseStatus")]
public int ResponseStatus
{ get; set; }
[DataMember(Name="responseDetails")]
public string ResponseDetails
{ get; set; }
[DataMember(Name = "responseData")]
public ResponseData Response
{ get; set; }
}
The response I get after making the request is:
{"responseData": {"language":"en","isReliable":false,"confidence":0.114892714}, "responseDetails": null, "responseStatus": 200}
and use this code to deserialize it:
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (Stream stream = response.GetResponseStream())
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(RequestResult));
RequestResult result = (RequestResult)serializer.ReadObject(stream);
stream.Close();
}
But am getting an exception stating "Cannot create an abstract class". Shouldnt it know about the DetectionResult class and properly deserialize it?
In your response data there is no way to infer a concrete type. The type to deserialize is not specified in the response.
From MSDN:
To preserve type identity, when serializing complex types to JSON a
"type hint" can be added, and the deserializer recognizes the hint and
acts appropriately. The "type hint" is a JSON key/value pair with the
key name of "__type" (two underscores followed by the word "type").
The value is a JSON string of the form
"DataContractName:DataContractNamespace" (anything up to the first
colon is the name). Using the earlier example, "Circle" can be
serialized as follows.
http://msdn.microsoft.com/en-us/library/bb412170.aspx
See the section related to polymorphism.
Have you tried putting the KnownType attribute on ResponseData instead of RequestResult?
In that code sample you need [KnownType(typeof(DetectionResult))] to be an attribute of ResponseData rather than RequestResult.
I don't know if that's sufficient to resolve your problem
From my experience working with the DataContractSerializer and the XmlSerializer, when an unexpected type is met during serialization process, those serializers throw an exception; they don't simply do the best they can. Maybe the DataContractJsonSerializer does not support KnownTypes at all.