Is there a way to get the raw/original JSON value from a JToken?
The problem:
var data = JObject.Parse(#"{
""SimpleDate"":""2012-05-18T00:00:00Z"",
""PatternDate"":""2012-11-07T00:00:00Z""
}");
var value = data["SimpleDate"].Value<string>();
The value is now 05/18/2012 00:00:00 but I need the original string 2012-05-18T00:00:00Z.
Is there a way to get this original value? Also, I cannot change the way how the JObject is created (e.g. change settings), because it is passed as parameter into my class...
(Reference: The original NJsonSchema issue)
You cannot get the original string, date strings are recognized and converted to DateTime structs inside the JsonReader itself. You can see this if you do:
Console.WriteLine(((JValue)data["SimpleDate"]).Value.GetType()); // Prints System.DateTime
You can, however, extract the dates in ISO 8601 format by doing:
var value = JsonConvert.SerializeObject(data["SimpleDate"]);
// value is "2012-05-18T00:00:00Z"
This will always output a JValue in a JSON-appropriate string format. Since your original dates are in this format, this may meet your needs.
(Honestly, I'm surprised JValue.ToString() outputs dates in non-ISO format, given that JObject.ToString() does output contained dates in ISO format.)
If you were able to change you settings while reading your JObject, you could use JsonSerializerSettings.DateParseHandling = DateParseHandling.None to disable DateTime recognition:
var settings = new JsonSerializerSettings { DateParseHandling = DateParseHandling.None };
var data = JsonConvert.DeserializeObject<JObject>(#"{
""SimpleDate"":""2012-05-18T00:00:00Z"",
""PatternDate"":""2012-11-07T00:00:00Z""
}", settings);
var value = data["SimpleDate"].Value<string>();
Debug.WriteLine(value); // Outputs 2012-05-18T00:00:00Z
There's no overload to JObject.Parse() that takes a JsonSerializerSettings, so use DeserializeObject. This setting eventually gets propagated to JsonReader.DateParseHandling.
Related Newtonsoft docs:
Json.NET interprets and modifies ISO dates when deserializing to JObject #862
Serializing Dates in JSON.
There's a solution I found in Json.NET Disable the deserialization on DateTime:
JsonReader reader = new JsonTextReader(new StringReader(j1.ToString()));
reader.DateParseHandling = DateParseHandling.None;
JObject o = JObject.Load(reader);
another approach - that would work - Regex
SimpleDate(?:.*):(?:.*?)\"([0-9|-]{1,}T[0-9|:]+Z)
it is a regex pattern to extract the data you look for - you just wanted the string, so here it is .
it is not the JSON parsing approach - but it indeed extracts the string.
here is a sample of how it works
Related
I have some dynamic object, data:
{
...
"start": "2022-09-24T04:04:00Z",
...
}
and I want to get the string value "2022-09-24T04:04:00Z" into a variable. Whenever I try casting, or printing or data.ToString etc, the string is automatically formatted to "24/09/2022 4:04:00 AM".
How can I convert this dynamic property to a string without changing its format?
It looks like it's being interpreted as a datetime, and then calling DateTime.ToString on it, which must convert the format.
The dynamic comes from JsonConvert.DeserializeObject<dynamic> btw.
You can turn off DateTime handling via JsonSerializerSettings:
static void Main(string[] args)
{
var json = "{\"start\": \"2022-09-24T04:04:00Z\"}";
var settings = new JsonSerializerSettings { DateParseHandling = DateParseHandling.None };
var result = JsonConvert.DeserializeObject<dynamic>(json, settings);
Console.WriteLine(result["start"]);
Console.ReadLine();
}
I've noticed that when parsing JSON using the Newtonsoft.Json.Linq namespace, selecting a date value returns a string in a different format to that of the original JSON. What is causing this?
For example:
JSON
[
{
...
"commit": {
...
"committer": {
"name": "GitHub",
"email": "noreply#github.com",
"date": "2016-12-19T11:53:13Z"
},
...
}
...
}
...
]
C#
...
List<Commit> commits = new List<Commit>();
JArray commitsArray = JArray.Parse(rawCommits);
...
foreach (var entry in commitsArray)
{
DateTime date;
CultureInfo provider = CultureInfo.InvariantCulture;
string format = "MM/dd/yyyy HH:mm:ss";
try
{
date = DateTime.ParseExact((string)entry["commit"]["committer"]["date"], format, provider);
}
catch (FormatException ex)
{
date = new DateTime(0);
}
...
}
...
rawCommits is a string representation of the raw JSON obtained using Microsoft.AspNetCore.WebUtilities.HttpRequestStreamReader().
I would expect (string)entry["commit"]["committer"]["date"] to return the same string as is in the JSON, in this instance in the format "yyyy-MM-ddTHH:mm:ssz" but as the above snippet shows it is instead in the format "MM/dd/yyyy HH:mm:ss". Why has the format changed and what has happened to the time and timezone identifiers?
The only thing I can think is that the call to JArray.Parse(string) identifies and manipulates the date. Is this the case? If so, surely it is undesirable behaviour? If not, what is going on?
Edit
This can be produced with the following example in an .Net Core Console App, adding "Microsoft.AspNetCore.Mvc": "1.1.0" to the project.json file:
using Newtonsoft.Json.Linq;
using System;
namespace JsonExample
{
public class Program
{
public static void Main(string[] args)
{
string json = "{\"date\": \"2016-12-19T11:53:13Z\"}";
JToken jToken = JToken.Parse(json);
Console.WriteLine(jToken["date"]);
Console.ReadLine();
}
}
}
With an output of 19/12/2016 11:53:13, which is interesting because it is yet another format (dd/MM/yyyy HH:mm:ss). Does this perhaps have anything to do with localisation settings? If so, why? It's also confusing, given that IIS Express is running on the same machine as that on which I executed the above code, yet I thought it took the host machines localisation. It also means if I deploy to a server with different localisation than my development machine the format specifier in the original post will because an exception and I'll end up with a value equal to new DateTime(0). What is it I am not understanding?
You are correct, the call to JArray.Parse is parsing the date for you automatically (by design).
I think to get at the raw string you need to use the reader directly:
var s = #"[{
""commit"": {
""committer"": {
""name"": ""GitHub"",
""email"": ""noreply#github.com"",
""date"": ""2016-12-19T11:53:13Z""
}
}
}
]";
using (var sr = new StringReader(s))
using (var jr = new JsonTextReader(sr) { DateParseHandling = DateParseHandling.None })
{
var arr = JArray.ReadFrom(jr);
foreach (var entry in arr)
{
Console.WriteLine(entry["commit"]["committer"]["date"].ToString()); // 2016-12-19T11:53:13Z
}
}
Json.Net stores date-looking values as dates internally, not as strings (it does the same for other base data types as well). So when you start to manipulate these values, you are actually dealing with the internal representation of that value which is a date type, and when you then get it out as a string, you're getting back a string conversion of that internal value, not the original string as you are expecting.
This is a notable difference between dealing with JSON in JS vs. using Json.Net in C# - an unfortunate one IMHO as it requires additional code to work around.
Is there a way to get the raw/original JSON value from a JToken?
The problem:
var data = JObject.Parse(#"{
""SimpleDate"":""2012-05-18T00:00:00Z"",
""PatternDate"":""2012-11-07T00:00:00Z""
}");
var value = data["SimpleDate"].Value<string>();
The value is now 05/18/2012 00:00:00 but I need the original string 2012-05-18T00:00:00Z.
Is there a way to get this original value? Also, I cannot change the way how the JObject is created (e.g. change settings), because it is passed as parameter into my class...
(Reference: The original NJsonSchema issue)
You cannot get the original string, date strings are recognized and converted to DateTime structs inside the JsonReader itself. You can see this if you do:
Console.WriteLine(((JValue)data["SimpleDate"]).Value.GetType()); // Prints System.DateTime
You can, however, extract the dates in ISO 8601 format by doing:
var value = JsonConvert.SerializeObject(data["SimpleDate"]);
// value is "2012-05-18T00:00:00Z"
This will always output a JValue in a JSON-appropriate string format. Since your original dates are in this format, this may meet your needs.
(Honestly, I'm surprised JValue.ToString() outputs dates in non-ISO format, given that JObject.ToString() does output contained dates in ISO format.)
If you were able to change you settings while reading your JObject, you could use JsonSerializerSettings.DateParseHandling = DateParseHandling.None to disable DateTime recognition:
var settings = new JsonSerializerSettings { DateParseHandling = DateParseHandling.None };
var data = JsonConvert.DeserializeObject<JObject>(#"{
""SimpleDate"":""2012-05-18T00:00:00Z"",
""PatternDate"":""2012-11-07T00:00:00Z""
}", settings);
var value = data["SimpleDate"].Value<string>();
Debug.WriteLine(value); // Outputs 2012-05-18T00:00:00Z
There's no overload to JObject.Parse() that takes a JsonSerializerSettings, so use DeserializeObject. This setting eventually gets propagated to JsonReader.DateParseHandling.
Related Newtonsoft docs:
Json.NET interprets and modifies ISO dates when deserializing to JObject #862
Serializing Dates in JSON.
There's a solution I found in Json.NET Disable the deserialization on DateTime:
JsonReader reader = new JsonTextReader(new StringReader(j1.ToString()));
reader.DateParseHandling = DateParseHandling.None;
JObject o = JObject.Load(reader);
another approach - that would work - Regex
SimpleDate(?:.*):(?:.*?)\"([0-9|-]{1,}T[0-9|:]+Z)
it is a regex pattern to extract the data you look for - you just wanted the string, so here it is .
it is not the JSON parsing approach - but it indeed extracts the string.
here is a sample of how it works
I am converting from XML to JSON using SerializeXmlNode. Looks the expected behavior is to convert all XML values to strings, but I'd like to emit true numeric values where appropriate.
// Input: <Type>1</Type>
string json = JsonConvert.SerializeXmlNode(node, Newtonsoft.Json.Formatting.Indented, true);
// Output: "Type": "1"
// Desired: "Type": 1
Do I need to write a custom converter to do this, or is there a way to hook into the serialization process at the appropriate points, through delegates perhaps? Or, must I write my own custom JsonConverter class to manage the transition?
Regex Hack
Given the complexity of a proper solution, here is another (which I'm not entirely proud of, but it works...).
// Convert to JSON, and remove quotes around numbers
string json = JsonConvert.SerializeXmlNode(node, Newtonsoft.Json.Formatting.Indented, true);
// HACK to force integers as numbers, not strings.
Regex rgx = new Regex("\"(\\d+)\"");
json = rgx.Replace(json, "$1");
XML does not have a way to differentiate primitive types like JSON does. Therefore, when converting XML directly to JSON, Json.Net does not know what types the values should be, short of guessing. If it always assumed that values consisting only of digits were ordinal numbers, then things like postal codes and phone numbers with leading zeros would get mangled in the conversion. It is not surprising, then, that Json.Net takes the safe road and treats all values as string.
One way to work around this issue is to deserialize your XML to an intermediate object, then serialize that to JSON. Since the intermediate object has strongly typed properties, Json.Net knows what to output. Here is an example:
class Program
{
static void Main(string[] args)
{
string xml = #"<root><ordinal>1</ordinal><postal>02345</postal></root>";
XmlSerializer xs = new XmlSerializer(typeof(Intermediary));
using (TextReader reader = new StringReader(xml))
{
Intermediary obj = (Intermediary)xs.Deserialize(reader);
string json = JsonConvert.SerializeObject(obj , Formatting.Indented);
Console.WriteLine(json);
}
}
}
[XmlRoot("root")]
public class Intermediary
{
public int ordinal { get; set; }
public string postal { get; set; }
}
Output of the above:
{
"ordinal": 1,
"postal": "02345"
}
To make a more generic solution, yes, you'd have to write your own converter. In fact, the XML-to-JSON conversion that takes place when calling SerializeXmlNode is done using an XmlNodeConverter that ships with Json.Net. This converter itself does not appear to be very extensible, but you could always use its source code as a starting point to creating your own.
I'd like parse JSON string and use the token.Type property to detect values of type JTokenType.TimeSpan.
I can't work out how to express the TimeSpan in my input string, everything seems to be interpreted as JTokenType.String.
var timeSpanString = TimeSpan.FromHours(1).ToString();
testString = string.Format(#"{{""Value"": ""{0}"" }}", timeSpanString);
var statObject = JObject.Parse(testString);
JToken token = statObject["Value"];
var tokenValue = token.ToString();
var tokenType = token.Type; // JTokenType.String
I even tried:
JValue jValue = new JValue("test");
jValue.Value = TimeSpan.FromHours(1);
bool isTimeSpan = jValue.Type == JTokenType.TimeSpan; // true!
testString = string.Format(#"{{""Value"": ""{0}"" }}", jValue.Value);
var statObject = JObject.Parse(testString);
JToken token = statObject["Value"];
var tokenValue = token.ToString();
var tokenType = token.Type; // JTokenType.String
Which at least produces a JValue object of tokenType JTokenType.TimeSpan, but still shows up as a JTokenType.String when I parse it.
This works perfectly for DateTime objects.
How can I express the input string such that the parsed value type is JTokenType.TimeSpan ?
Based on what I've seen while using JSON.NET for a while now, you will never, with the default settings, parse a string and retrieve a token with type JTokenType.TimeSpan (same for some other types as well, such as Guid or Uri). I have a pretty good guess of why this is the case (based on my experience working a few years ago with the DataContractJsonSerializer).
Basically, it's a matter of how much information the parser can retrieve out of the input. JSON is a very simple syntax which only knows about numbers, boolean and strings (in addition to arrays and objects). Many CLR types don't have a native JSON type (Uri, DateTime, DateTimeOffset, TimeSpan, and so on), so when any JSON parser is reading the data, it will try to use the best match.
If you're deserializing the JSON string into a CLR data type, then the serializer has some additional information that it can use to disambiguate what a JSON string maps to - the type of the field / property that value is being deserialized to. However, when you're deserializing a JSON data to a JToken object graph, there's no additional information, and JSON.NET has to choose one type. The most natural type to deserialize a JSON string is, well, a CLR string.
But why do dates are deserialized correctly as JTokenType.Date? IIRC, the JSON.NET reader has a special code for dates (controlled by the DateParseHandling enumeration), which tries to match the parsed strings to some predefined formats (either ISO 8601 or the old Microsoft ASP.NET AJAX format), and if it finds a string which match it, it will read it as a DateTime (or DateTimeOffset) instead of a string. I don't know whether it's possible to extend that behavior to also support TimeSpan or other types, but I wouldn't be surprised, since the extensibility in JSON.NET is quite good.
If you are trying to parse a TimeSpan it needs to be surrounded in quotations:
'"12:00:00"'
If you serialize a TimeSpan and look at the string result it looks like:
"\"12:00:00\""
At least this worked for me using NewtonSoft.JsonConvert. The string in my DB "12:00:00" (including the quotes).
And using JsonConvert.DeserializeObject(dbString) returns fine.
Just ran into the same problem and was able to do it this way:
string json = "{ \"span\": \"00:00:15\"}";
JToken token = JToken.Parse(json);
TimeSpan span = token["span"].ToObject<TimeSpan>();