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.
Related
I don't want to use Newtonsoft's Json.Net library. I'm avoiding any third-party dependencies if I can help it in this project.
If I have JSON that looks like this:
{
"has_more_items": false,
"items_html": "...",
"min_position": "1029839231781429248"
}
and I have a class that looks like this:
public class TwitterJson
{
bool hasMore { get; set; } // has_more_items
string rawText { get; set; } // items_html
string nextKey { get; set; } // min_position
}
and I have a JsonObject containing the above JSON:
JsonObject theJson = JsonObject.Parse(result);
How do I deserialize the JsonObject into my class? I've been trying to find a clear example of this, and everything I've found uses Json.Net.
I've been trying to find a clear example of this, and everything I've found uses Json.Net.
Because reinventing existing functionality is a waste of time especially when all the hard work has already been done for you.
If you insist on not using it then you will have to manually construct the object model based on the expected JSON.
For example, assuming publicly available properties
public class TwitterJson {
public bool hasMore { get; set; } // has_more_items
public string rawText { get; set; } // items_html
public string nextKey { get; set; } // min_position
}
Then parsing the above to the desired object model
JsonObject theJson = JsonObject.Parse(result);
var model = new TwitterJson {
hasMore = theJson.GetNamedBoolean("has_more_items"),
rawText = theJson.GetNamedString("items_html"),
nextKey = theJson.GetNamedString("min_position")
};
As mentioned by #Dimith, you need to decorate your class with [DataContract] and [DateMember], Please refer to below code which will convert your JSON into a given object.
// Deserialize a JSON string to a given object.
public static T ReadToObject<T>(string json) where T: class, new()
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
return ser.ReadObject(stream) as T;
}
}
Class:
[DataContract]
public class TwitterJson
{
[DataMember(Name = "has_more_items")]
bool hasMore { get; set; } // has_more_items
[DataMember(Name = "items_html")]
string rawText { get; set; } // items_html
[DataMember(Name = "min_position")]
string nextKey { get; set; } // min_position
}
Sample on how to use:
var result = "{\"has_more_items\": false, \"items_html\": \"...\",\"min_position\": \"1029839231781429248\"}";
var obj = ReadToObject<TwitterJson>(result);
You have to decorate your class with [DataContract] and [DataMember] attributes. Write the json into a memory stream and deserialize using DataContractJsonSerializer
Here is a more elaborated sample.
In addition to #Nkosi's answer below are some Comparisons between JSON.net and other alternatives:
JSON.Net vs DataContractJsonSerializer
JSON.Net vs Windows.Data.Json
I have a class which basically contains a property like this:
public class Msg
{
[JsonProperty(PropertyName = "XD1703301059485299")]
public Shipping shipping { get; set; }
}
the problem is in this part:
[JsonProperty(PropertyName = "XD1703301059485299")]
And the dynamic property name that I get from server...
This property name can be any name that server returns. In this particular case it's able to map the JSON to my class since the property names are same... But when server returns something like this:
XS12394124912841
The object is the null....
How can I resolve property name to be dynamic ? Can someone help me out?
P.S. This is the JSON response itself:
{"status":1,"msg":{"dynamic_name":{"order_sn":"12312313123123123","order_status":"0","shipping_info":[{"shipping_name":"","shipping_no":"","shipping_img":"","shipping_code":"","shipping_time":"","track_goods":""}]}},"errcode":0}
So I don't think this problem is as dynamic as it sounds. You can probably just convert to a dyanmic object and explicitly handle conversions.
Sample solution below. I inserted a few values to show conversion works as expected.
Add nuget package Newtonsoft.Json
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace Serialize
{
public class Shipping
{
[JsonProperty(PropertyName = "shipping_name")]
public String Name { get; set; }
[JsonProperty(PropertyName = "shipping_img")]
public String Img { get; set; }
[JsonProperty(PropertyName = "shipping_code")]
public String Code { get; set; }
}
public class Order
{
public Shipping shipping { get; set; }
[JsonProperty(PropertyName = "order_sn")]
public string SerialNumber { get; set; }
[JsonProperty(PropertyName = "order_status")]
public string Status { get; set; }
}
class Program
{
static void Main(string[] args)
{
/*
{
"status":1,
"msg": {
"dynamic_name": {
"order_sn": "12312313123123123",
"order_status":"0",
"shipping_info": [{
"shipping_name":"name",
"shipping_no":"",
"shipping_img":"img",
"shipping_code":"code",
"shipping_time":"",
"track_goods":""
}]
}
},
"errcode":0
}
* */
var raw = "{ \"status\":1, \"msg\":{\"dynamic_name\":{\"order_sn\":\"12312313123123123\",\"order_status\":\"0\",\"shipping_info\":[{\"shipping_name\":\"name\",\"shipping_no\":\"\",\"shipping_img\":\"img\",\"shipping_code\":\"code\",\"shipping_time\":\"\",\"track_goods\":\"\"}]}},\"errcode\":0}";
var incomingOrder = new Order();
// properties on dynamic objects are evaluated at runtime
dynamic msgJson = JObject.Parse(raw);
// you'll want exception handling around all of this
var order = msgJson.msg.dynamic_name;
// accessing properties is easy (if they exist, as these do)
incomingOrder.SerialNumber = order.order_sn;
incomingOrder.Status = order.order_status;
// JObject cast might not be necessary. need to check for array elements, etc.
// but it's simple to serialize into a known type
incomingOrder.shipping = ((JObject)(order.shipping_info[0])).ToObject<Shipping>();
}
}
}
Alternatively, if the property name is given at runtime, you can dereference properties with the indexer getter
dynamic msgJson = JObject.Parse(raw);
JObject order = msgJson.msg["XS12394124912841"];
incomingOrder.SerialNumber = order["order_sn"].ToObject<string>();
incomingOrder.Status = order["order_status"].ToObject<string>();
incomingOrder.shipping = order["shipping_info"][0].ToObject<Shipping>();
You can implement something like this with the help of System.Web.Helpers
using (StreamReader r = new StreamReader("sample.json"))
{
string json = r.ReadToEnd();
dynamic data = Json.Decode(json);
Console.WriteLine(data["your_property"]);
}
Here sample.json contains your sample JSON response.
So, - in my DocumentDB I may have the following document:
{
"id": 1,
"type": "A",
"content": {
"x": 1,
"y": 2
}
}
That may be backed by this model:
public class acontent
{
public int x { get; set; }
public int y { get; set; }
}
public class document
{
public int id { get; set; }
public string type { get; set; }
public object content { get; set; }
}
public class documenta : document
{
public new acontent content { get; set; }
}
The idea here is that document is a complex object where content may vary depending on type.
Now, - in my ServiceFabric application I have a stateless microservice that reads from DocumentDB and should return a document type object when called from the ServiceProxy.
The problem in this is that the DocumentQuery from the DocumentDB SDK, uses Json.NET serializer when querying the database, whilst servicefabric uses DataContractSerializer for serializing the service-messages.
So when the content part of document class is being deserialized from the DocumentDB it becomes:
Newtonsoft.Json.Linq.JObject
But when it is serialized back through the returned service-message you get the exception:
Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data
contract which is not supported. Consider modifying the definition of
collection 'Newtonsoft.Json.Linq.JToken' to remove references to
itself.
To illustrate this issue try the folowing code:
using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;
using Newtonsoft.Json;
namespace jsoinissue
{
public class acontent
{
public int x { get; set; }
public int y { get; set; }
}
public class document
{
public int id { get; set; }
public string type { get; set; }
public object content { get; set; }
}
public class documenta : document
{
public new acontent content { get; set; }
}
public class Program
{
private const string JSON_A = "{\"id\":1,\"type\":\"A\",\"content\":{\"x\":1,\"y\":2}}";
private static string SerializeObject<T> (T obj)
{
try
{
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(T));
using (var ms = new MemoryStream())
{
js.WriteObject(ms, obj);
ms.Position = 0;
using (var sr = new StreamReader(ms))
return sr.ReadToEnd();
}
}
catch (Exception e)
{
return String.Format("EXCEPTION: {0}",e.Message);
}
}
public static void Main()
{
var A = JsonConvert.DeserializeObject<document>(JSON_A);
var a = SerializeObject<document>(A);//HERE BE TROUBLE
Console.WriteLine(a);
Console.ReadKey();
}
}
}
How could I best resolve this issue?
Your basic problem is that DataContractJsonSerializer does not support untyped, free-form JSON data. As explained in Working with untyped JSON in a WCF service the System.Json namespace was added to Silverlight for this purpose, but it seems that it never made it into the full .Net class library.
Instead, in your stateless microservice can do a nested serialization where the free-form JSON is represented as an escaped string literal when serializing using the data contract serializer. Thus your classes would look something like this:
[DataContract]
[JsonObject]
public abstract class documentbase
{
[DataMember]
[JsonProperty]
public int id { get; set; }
[DataMember]
[JsonProperty]
public string type { get; set; }
[IgnoreDataMember]
[JsonProperty("content")]
public abstract JToken JsonContent { get; set; }
[JsonIgnore]
[DataMember(Name = "content")]
string DataContractContent
{
get
{
if (JsonContent == null)
return null;
return JsonContent.ToString(Newtonsoft.Json.Formatting.None);
}
set
{
if (string.IsNullOrEmpty(value))
JsonContent = null;
else
JsonContent = JToken.Parse(value);
}
}
}
[DataContract]
[JsonObject]
public class document : documentbase
{
JToken content;
public override JToken JsonContent { get { return content; } set { content = value; } }
}
[DataContract]
[JsonObject]
public class document<T> : documentbase where T : class
{
[IgnoreDataMember]
[JsonIgnore]
public T Content { get; set; }
public override JToken JsonContent
{
get
{
if (Content == null)
return null;
return JToken.FromObject(Content);
}
set
{
if (value == null || value.Type == JTokenType.Null)
Content = null;
else
Content = value.ToObject<T>();
}
}
}
Then the JSON generated by SerializeObject<document>(A) will look like:
{
"content":"{\"x\":1,\"y\":2}",
"id":1,
"type":"A"
}
Then, on the receiving system, you can deserialize to a document using the data contract serializer, then query the deserialized JToken JsonContent with LINQ to JSON. Alternatively, if the receiving system knows to expect a document<acontent> it can deserialize the data contract JSON as such, since document and document<T> have identical data contracts.
Have you looked into changing away from DataContractSerializer to a serializer with better support instead? Here's how you'd plug in a different serializer.
class InitializationCallbackAdapter
{
public Task OnInitialize()
{
this.StateManager.TryAddStateSerializer(new MyStateSerializer());
return Task.FromResult(true);
}
public IReliableStateManager StateManager { get; set; }
}
class MyStatefulService : StatefulService
{
public MyStatefulService(StatefulServiceContext context)
: this(context, new InitializationCallbackAdapter())
{
}
public MyStatefulService(StatefulServiceContext context, InitializationCallbackAdapter adapter)
: base(context, new ReliableStateManager(context, new ReliableStateManagerConfiguration(onInitializeStateSerializersEvent: adapter.OnInitialize)))
{
adapter.StateManager = this.StateManager;
}
}
This could be newtonsoft or whatever. Also I believe that the method is currently marked "Deprecated" however there's no alternative, so if it solves your problem go ahead and use it.
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.
I am trying to post some data to my MVC 3 controller through a hidden text field that contains some JSON. I have that JSON passed in via string coursesList. Anyone have an idea why this is not working?
All I'm doing is making a byte [] out of the JSON string, writing it to a MemoryStream, and deserializing that stream -- or, attempting to. BookCourse bc always ends up with null properties.
Here's something like the JSON I would be using:
[{"coursesection":"1234","netlogon":"jsmith","label":"CRSE-1313 Generic Course Titling ~ Joe Smith"}]
And here's the object to be deserialized into:
using System.Runtime.Serialization;
namespace xxxx.Models
{
[DataContract]
public class BookCourse
{
[DataMember]
public string coursesection { get; set; }
[DataMember]
public string netlogon { get; set; }
[DataMember]
public string label { get; set; }
}
}
Finally, the controller action code to do it --
var byteArray = Encoding.ASCII.GetBytes(coursesList);
// Deserialize byte array to data type
var stream = new MemoryStream();
stream.Write(byteArray, 0, byteArray.Length);
var crs = new DataContractJsonSerializer(typeof(BookCourse));
stream.Position = 0;
// Read stream to object
ad.CourseSectionIDs = new List<int>();
try
{
var bc = (BookCourse) crs.ReadObject(stream);
while (bc.coursesection != null)
{
cs.AssociateCourseBook(bc.netlogon, bc.coursesection, ad.ISBN);
bc = (BookCourse)crs.ReadObject(stream);
}
}
catch (System.Runtime.Serialization.SerializationException e)
{
// Is this best practice for handling "none"?
}
Your JSON string represents a collection of BookCourse, not a single BookCourse. So adapt your code:
var serializer = new DataContractJsonSerializer(typeof(BookCourse[]));
and then:
var bookCourses = (BookCourse[])crs.ReadObject(stream);
or if you want to work with a single BookCourse you will need to change your JSON string and remove the wrapping square brackets which represent a collection.
Darin is correct, here is the change if you want to do it on a contract level.
[DataContract]
public class BookCourse
{
[DataMember]
public string coursesection { get; set; }
[DataMember]
public string netlogon { get; set; }
[DataMember]
public string label { get; set; }
}
[DataContract]
public class BookCourceCollection
{
[DataMember]
public List<BookCourse> Collection;
public static BookCourceCollection ReturnCollection(string jsonString)
{
MemoryStream ms;
BookCourceCollection collection;
using (ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(BookCourceCollection));
collection = ser.ReadObject(ms) as BookCourceCollection;
}
return collection;
}
}
Usage:
string jsonString = "Your JSON string from the front end";
var bookCourceObject = BookCourseCollection.ReturnCollection(jsonString);
foreach (BookCourse bookCourse in bookCourceObject.Collection)
{
cs.AssociateCourseBook(bookCourse.netlogon, bookCourse.coursesection, bookCourse.ISBN);
}