Creating JObject with JObject.FromObject ignores DateFormatString - c#

I am trying to create a Newtonsoft JObject with a custom DateFormatSting ("yyyy-MM-ddTHH:mm:ssZ") using JOjbect.FromObject and I think there is a bug. My code is:
JObject jBytes = JObject.FromObject(myObject, MyJsonSerializer);
Here, JObject.FromObject seems to ignore the DateFormatString in my custom JsonSerializer.
I have a workaround, but still curious if I am doing something wrong, or if anyone else has seen this?
(workaround)
JObject jBytes = Object.Parse(JsonConvert.SerializeObject(myObject, MyDateFormatString);

The reason you are seeing this is that JValue stores the DateTime as an actual DateTime structure copied from your object, not as a string. Therefore the DateFormatString is not applied during the conversion to JToken hierarchy. You can verify this by doing the following:
var types = jBytes.DescendantsAndSelf().OfType<JValue>().Where(v => v.Type == JTokenType.Date).Select(v => v.Value.GetType().FullName);
Debug.WriteLine(JsonConvert.SerializeObject(types, Formatting.None));
The output will be ["System.DateTime", ...].
Thus the setting needs to be applied when you convert your JToken to its ultimate JSON string representation. Unfortunately, there doesn't seem to be a convenient ToString() overload on JToken allowing a DateFormatString to be specified. One possibility would be to serialize the root token:
var settings = new JsonSerializerSettings { DateFormatString = "yyyy/MM/dd" };
var jBytes = JObject.FromObject(myObject);
var json = JsonConvert.SerializeObject(jBytes, Formatting.Indented, settings); // Outputs "2015/09/24"
This at least avoids the parsing overhead of JToken.Parse() in your workaround.
Another option would be introduce an extension method modeled on JToken.ToString() and the TraceJsonWriter constructor that takes a JsonSerializerSettings and applies the appropriate settings:
public static class JsonExtensions
{
public static string ToString(this JToken token, Formatting formatting = Formatting.Indented, JsonSerializerSettings settings = null)
{
using (var sw = new StringWriter(CultureInfo.InvariantCulture))
{
using (var jsonWriter = new JsonTextWriter(sw))
{
jsonWriter.Formatting = formatting;
jsonWriter.Culture = CultureInfo.InvariantCulture;
if (settings != null)
{
jsonWriter.DateFormatHandling = settings.DateFormatHandling;
jsonWriter.DateFormatString = settings.DateFormatString;
jsonWriter.DateTimeZoneHandling = settings.DateTimeZoneHandling;
jsonWriter.FloatFormatHandling = settings.FloatFormatHandling;
jsonWriter.StringEscapeHandling = settings.StringEscapeHandling;
}
token.WriteTo(jsonWriter);
}
return sw.ToString();
}
}
}
Then you can just do:
var settings = new JsonSerializerSettings { DateFormatString = "yyyy/MM/dd" };
var json = jBytes.ToString(Formatting.Indented, settings); // Outputs "2015/09/24"
Prototype fiddle.

Related

Json.net Parse function deserialize Json String remove timestamp trailing zero [duplicate]

Here is the code:
string s = "2012-08-08T01:54:45.3042880+00:00";
JObject j1 = JObject.FromObject(new
{
time=s
});
Object o = j1["time"];
We can check that o is string and equals "2012-08-08T01:54:45.3042880+00:00"
Now we transfer j1.ToString() to another program, which is
{
"time": "2012-08-08T01:54:45.3042880+00:00"
}
then at the other program, try to parse it back to JObject, which is
JObject j2 = JObject.Parse(j1.ToString());
Object o2 = j2["time"];
Now, if we check o2, o2's type is Date, o2.ToString() is 8/7/2012 9:54:45 PM.
My question is:
Is there is way to disable the Date deserialization for JObject.Parse , and just get the raw string?
Thanks in advance
When parsing from an object to JObject you can specify a JsonSerializer which instructs how to handle dates.
JObject.FromObject(new { time = s },
new JsonSerializer {
DateParseHandling = DateParseHandling.None
});
Unfortunately Parse doesn't have this option, although it would make sense to have it. Looking at the source for Parse we can see that all it does is instantiate a JsonReader and then passes that to Load. JsonReader does have parsing options.
You can achieve your desired result like this:
using(JsonReader reader = new JsonTextReader(new StringReader(j1.ToString()))) {
reader.DateParseHandling = DateParseHandling.None;
JObject o = JObject.Load(reader);
}
For background, see Json.NET interprets and modifies ISO dates when deserializing to JObject #862, specifically this comment from JamesNK: I have no plans to change it, and I would do it again if give the chance.
You can accomplish this using JsonConvert.DeserializeObject as well, by using JsonSerializerSettings:
string s = "2012-08-08T01:54:45.3042880+00:00";
string jsonStr = $#"{{""time"":""{s}""}}";
JObject j1 = JsonConvert.DeserializeObject<JObject>(jsonStr, new JsonSerializerSettings {DateParseHandling = DateParseHandling.None});

Deserialize JSON using Newtonsoft.Json's JsonConvert.DeserializeObject<string>(jsonString)

I was been working with an extension method using generics and Newtonsoft.Json's JsonConvert.DeserializeObject<TSource>(jsonString)
the serialization works as expected
string value = string.Empty;
value = JsonConvert.SerializeObject(null); //"null"
value = JsonConvert.SerializeObject("null"); //"null"
value = JsonConvert.SerializeObject("value"); //"value"
value = JsonConvert.SerializeObject(""); //""
But when trying to Deserialize
string result = string.Empty;
result = JsonConvert.DeserializeObject("null"); //null, ok
result = JsonConvert.DeserializeObject("value"); //throwing error, expecting "value"
result = JsonConvert.DeserializeObject(""); //throwing error, expecting string.empty
Error: Unexpected character encountered while parsing value: v. Path '', line 0, position 0.
now I'm used where TSource : new () on the extension method, so that any string return types would be restricted as
public static TSource ExecuteScriptForData(this IJavaScriptExecutor javaScriptExecutor, string script, params object[] args) where TSource : new ()
which is not letting me to use interfaces like IList, IPerson or ReadOnlyCollection on TSource
Now Is there any way to configure the Deserializer so that it would be able to Deserialize strings as Serializer is producing ?
Now Is there any way to configure the Deserializer so that it would be able to deserialize strings as Serializer is producing ?
You can use JsonSerializerSettings's TypeNameHandling property.
var settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All };
var str = JsonConvert.SerializeObject(null,settings);
var obj1 = JsonConvert.DeserializeObject(str, settings);
str = JsonConvert.SerializeObject("value", settings);
var obj2 = JsonConvert.DeserializeObject(str, settings);
str = JsonConvert.SerializeObject("", settings);
var obj3 = JsonConvert.DeserializeObject(str, settings);
value in json does not have any meanings. If you expect your result to be the value of string you have to put it in qoutes:
string result = string.Empty;
result = JsonConvert.DeserializeObject<string>("null"); //null, ok
result = JsonConvert.DeserializeObject<string>("'value'"); // "value"
result = JsonConvert.DeserializeObject<string>("''"); // string.Empty

How to return unparsed string instead of DateTime from JProperty [duplicate]

Here is the code:
string s = "2012-08-08T01:54:45.3042880+00:00";
JObject j1 = JObject.FromObject(new
{
time=s
});
Object o = j1["time"];
We can check that o is string and equals "2012-08-08T01:54:45.3042880+00:00"
Now we transfer j1.ToString() to another program, which is
{
"time": "2012-08-08T01:54:45.3042880+00:00"
}
then at the other program, try to parse it back to JObject, which is
JObject j2 = JObject.Parse(j1.ToString());
Object o2 = j2["time"];
Now, if we check o2, o2's type is Date, o2.ToString() is 8/7/2012 9:54:45 PM.
My question is:
Is there is way to disable the Date deserialization for JObject.Parse , and just get the raw string?
Thanks in advance
When parsing from an object to JObject you can specify a JsonSerializer which instructs how to handle dates.
JObject.FromObject(new { time = s },
new JsonSerializer {
DateParseHandling = DateParseHandling.None
});
Unfortunately Parse doesn't have this option, although it would make sense to have it. Looking at the source for Parse we can see that all it does is instantiate a JsonReader and then passes that to Load. JsonReader does have parsing options.
You can achieve your desired result like this:
using(JsonReader reader = new JsonTextReader(new StringReader(j1.ToString()))) {
reader.DateParseHandling = DateParseHandling.None;
JObject o = JObject.Load(reader);
}
For background, see Json.NET interprets and modifies ISO dates when deserializing to JObject #862, specifically this comment from JamesNK: I have no plans to change it, and I would do it again if give the chance.
You can accomplish this using JsonConvert.DeserializeObject as well, by using JsonSerializerSettings:
string s = "2012-08-08T01:54:45.3042880+00:00";
string jsonStr = $#"{{""time"":""{s}""}}";
JObject j1 = JsonConvert.DeserializeObject<JObject>(jsonStr, new JsonSerializerSettings {DateParseHandling = DateParseHandling.None});

After deserialization cannot cast object

I am serializing and then deserializing a Dictionary in Newtonsoft.Json, however the output is not what I expected and is throwing exceptions. Can someone let me know what I'm doing wrong?
Here is my code
using Newtonsoft.Json;
namespace Task1
{
public class Class1
{
public static void Main()
{
var dict = new Dictionary<string, object>();
dict["int"] = new GenericItem<int> {CreatedAt = DateTime.Now, Object = 111};
dict["string"] = new GenericItem<string> {CreatedAt = DateTime.Now.Date, Object = "test test"};
var json = JsonConvert.SerializeObject(dict);
var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var test = dict["int"] is GenericItem<int>;
var test2 = dict["int"] as GenericItem<int>;
var test3 = (GenericItem<int>) dict["int"];
var desTest = desDict["int"] is GenericItem<int>;
var desTest2 = desDict["int"] as GenericItem<int>;
var desTest3 = (GenericItem<int>) desDict["int"];
}
}
public class GenericItem<T>
{
public DateTime CreatedAt { get; set; }
public T Object { get; set; }
}
}
First three tests return True, Instance of GenericItem<Int> and again Instance of GenericItem<Int>.
But after deserialization return false, null, and InvalidCastException.
What is the problem? Why after deserealization does it throw InvalidCastException?
What is the problem? Why after deserealization does it throw InvalidCastException?
The reason of this behaviour is simple - if you check the type of this element with desDict["int"].GetType(), you will see that it returns Newtonsoft.Json.Linq.JObject and that's definitely not the type you were expecting.
It is possible to do what you want with usage of TypeNameHandling parameter of JsonSerializerSettings class, as suggested in this SO answer. Change this:
var json = JsonConvert.SerializeObject(dict);
var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
to this:
var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;
var json = JsonConvert.SerializeObject(dict, settings);
var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings);
Info from reffered answer:
The TypeNameHandling flag will add a $type property to the JSON, which
allows Json.NET to know which concrete type it needs to deserialize
the object into. This allows you to deserialize an object while still
fulfilling an interface or abstract base class.
The downside, however, is that this is very Json.NET-specific. The
$type will be a fully-qualified type, so if you're serializing it with
type info,, the deserializer needs to be able to understand it as
well.
As an alternative you can loop throught the deserialized dictionary to convert values for each key from JObject to desired type:
var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var newDict = new Dictionary<string, object>(); //as you can't modify collection in foreach loop and the original dictionary can be out of scope of deserialization code
foreach (var key in desDict.Keys)
{
switch(key)
{
case "int":
newDict[key] = ((JObject)desDict[key]).ToObject<GenericItem<int>>();
break;
case "string":
newDict[key] = ((JObject)desDict[key]).ToObject<GenericItem<string>>();
break;
}
}
desDict = newDict;
You don't need that manual solution here, but maybe someday this knowledge will be useful. I found info about JOobject.ToObject() method in this SO answer. I've checked both methods and both work fine with the code you provided.
You ask it to deserialize to a Dictionary<string, object> so it does so. It doesn't store the type information so it builds an object to store the deserialized information in.
I don't think you can get what you want, due to variance issues. You could deserialize to a Dictionary<string, GenericItem<object>> but that doesn't seem to be what you wanted. You get a json.net specific object in place of your int or string. You could cast that to what you want.
try :
var desDict = JsonConvert.DeserializeObject<Dictionary<string, GenericItem<object>>>(json);
or :
var desDict = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);
using dynamic you don't need to cast your object at all, you can just use it's properties

Deserializing an unknown type in JSON.NET

I just got a hold of JSON.NET and its been great so far.
However, I cannot figure out how to determine the type of a serialized object when deserializing it.
How can I determine the object's class to cast it?
To clarify my question, let's say I wanted to do this
string json = <<some json i don't know>>
var data = JsonConvert.DeserializeObject(json);
if (data is Person)
{
//do something
}
else if (data is Order)
{
//do something else
}
Does Json.NET support this kind of functionality?
you can use dynamic type
JsonConvert.DeserializeObject<dynamic>(JSONtext)
it may help you
IDictionary < string, JToken > Jsondata = JObject.Parse(yourJsonString);
foreach(KeyValuePair < string, JToken > element in Jsondata)
{
string innerKey = element.Key;
if (element.Value is JArray)
{
// Process JArray
}
else if (element.Value is JObject)
{
// Process JObject
}
}
In case you control the serialization, you can use the TypeNameHandling setting
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var toBeSerialized = settings; // use the settings as an example data to be serialized
var serialized = JsonConvert.SerializeObject(toBeSerialized, Formatting.Indented, settings);
var deserialized = JsonConvert.DeserializeObject(serialized, settings);
var deserializedType = deserialized.GetType().Name; // JsonSerializerSettings
for anyone still trying to do this, I suggest to use
JsonConvert.DeserializeObject<ExpandoObject>(JSONtext)

Categories

Resources