Consider the following JSON object:
{
"value": 0
}
Now suppose I'm mapping this to a .NET type Foo:
class Foo
{
public double Value { get; set; }
}
The type of Foo.Value is double, because Value isn't always an integer value.
Using JSON.NET, this works beautifully:
Foo deserialized = JsonConvert.DeserializeObject<Foo>(json);
However, observe what happens when I try to convert the object back to its JSON representation:
string serialized = JsonConvert.SerializeObject(deserialized, Formatting.Indented);
Output:
{
"Value": 0.0
}
Notice the trailing zero? How do I get rid of it?
EDIT
I suspect that the answer will be write your own converter. If it is, then that's fine and I guess I'll accept that as the answer. I'm just wondering if perhaps there exists an attribute that I don't know of that lets you specify the output format (or similar).
It appears that this is a hard-coded behavior of the library:
https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/JsonConvert.cs#L300
If you want to alter the behavior you'll need to edit the library and recompile from source (or choose another JSON library)
Related
I use System.Text.Json to deserialize some stuff and then serialize it. The problem is that for example double value 99.6 is being deserialized and then serialized to 99.599999999999994.
What can I do about it?
Here's a reproduction in console app.
using System;
using System.Text.Json;
namespace ConsolePG3
{
class Program
{
static void Main(string[] args)
{
Person person = new Person { Value = 99.6 };
var text = JsonSerializer.Serialize(person);
Console.WriteLine(text);
Console.ReadLine();
}
}
class Person
{
public double Value { get; set; }
}
}
The important thing to get your head around here is that the double with value 99.6 does not exist, and never existed. You imagined it. It was rounded the moment you compiled it. It is simply not possible to represent the exact value 99.6 in floating-point arithmetic, due to how floating-point works. The serializer has correctly serialized the actual value that exists.
If you want to represent discreet values in the way that humans tend to think of them - use decimal instead of floating-point (float, double). It (decimal) is also limited in terms of precision (and it is not CPU-optimized), but the way it approximates is much more comparable to how humans approximate, and it will readily story the exact value for most common scenarios.
Frankly, the moment you are thinking about "the exact value": floating point is not a good choice.
I have a Version field that is a part of a data contract, like so:
[DataMember(EmitDefaultValue = false)]
public Version version { get; set; }
In a REST request, that ends up getting serialized as
"version": {
"_Build": 0,
"_Major": 1,
"_Minor": 0,
"_Revision": 0
}
which is fine by me. However, I start a new project, add the project as a reference, and then when I try to send a REST request from the new project, it gets serialized as
"version":{
"Major":1,
"Minor":0,
"Build":0,
"Revision":-1,
"MajorRevision":-1,
"MinorRevision":-1
}
which, of course, garners me a BAD_REQUEST. Note: I am worried here about the different field names, not the actual numbers. I'm using the same Version type in both.
The negative one (-1) in your revision values does mean that, the revision number is undefined.
Look for (-1) in below link.
https://msdn.microsoft.com/en-us/library/system.version.revision(v=vs.110).aspx
And the attribute EmitDefaultValue=false will work (whether to serialize the default value) only for default values.
Here in your case, "-1" is not a default value for integer types (Revision data type). It is zero.
May be to handle this case, use OnSerializing() attribute and customize the datamembers.
Roughly,
Function lifted from (https://msdn.microsoft.com/en-us/library/system.runtime.serialization.onserializingattribute(v=vs.110).aspx )
[OnSerializing()]
internal void OnSerializingMethod(StreamingContext context)
{
if (this.version.Revision == -1)
{
//handle it properly
or
this.version.Revision = default(int)// will not be serialized as you are using "EmitDefaultValue=false"
}
}
So I've been looking to convert my Json array into an array of objects in Unity. I've found my solution from a 2 yeard old thread without explanation but I'm curious as to how it actually works.
If I use Visual Studio to look for the definition of FromJson it shows me this
public static T FromJson<T>(string json);
As I understand is that FromJson asks for an object to be filled, I give the class MyWrapper but besides telling MyWrapper that he contains a list of Question I never ask it to create a new item in the list. So how does it actually fill the list?
C#
MyWrapper wrappedQuestions = JsonUtility.FromJson<MyWrapper>(jsonString);
[Serializable]
public class MyWrapper
{
public List<Question> questions;
}
[Serializable]
public class Question
{
public int questionType;
public string questionString;
public int questionAnswer;
}
Json
{
"questions": [
{
"questionType": 1,
"questionString": "4^2",
"questionAnswer": 16
},
{
"questionType": 2,
"questionString": "√(25)",
"questionAnswer": 5
}
]
}
I'm still a beginner programmer so I hope I'am able to ask such questions here.
If you wonder why you need a wrapper for that, that's simply because Unity engineers did not add direct support for primitive types or arrays. It's just how they programmed it. Most Json API are not like this.
So how does it actually fill the list?
Reflection.
1.It reads the json you passed to it. It detects the questions variable in the json. It detects that the questions variable is an array due to the format such as [] and the commas that separates each item.
2.It finds the type of that questions variable which is Question or List of Question.
3.It uses Activator.CreateInstance to create new instance of Question, read each value from each variable in the json and then fill them up with reflection with that new instance it created.
4. It returns the new instance that is filled.
If you read and understand how to do basic stuff with reflection in C#, you may be able to make your own simple Json parser with the Queue class.
Finally, you can use the JsonHelper wrapper from this answer to serialize/de-serialize arrays easily without having to make a wrapper for each class.
As JSON stands for Javascript Object Notation, JSON objects (strings) follow a pattern. For any string to parsed and converted to the object, it has to be a valid JSON. It has certain rules/syntax,
for example
[ ] is for array/list,
{ } is for objects
and every object can contain key-value pairs (separated by colon :) to represent variables along with their values
{ "Key" : "Value" }
Now JSON parser is aware of these rules so it can check if any string is valid JSON.
What does it need to know to convert a JSON into a class object?
The class type you provide here :
JsonUtility.FromJson<MyWrapper>(jsonString);
is MyWrapper.
It should have the same structure as your jsonString.
Let's break down your jsonString to map it with the class structure:
This represents a MyWrapper object. which contains only one property called questions, which is an empty list.
{
"questions": [ ]
}
if questions have any element in it, it would be of type Question we can write it as following in JSON:
{
"questionType": 1, // int value
"questionString": "4^2", // string value
"questionAnswer": 16 // int value
}
now if questions have more than one elements they would be separated by a comma:
"questions": [
{
"questionType": 1,
"questionString": "4^2",
"questionAnswer": 16
},
{
"questionType": 2,
"questionString": "√(25)",
"questionAnswer": 4
},
...
]
JSON parser is aware of all these rules so by looking at class structure it can parse the string and create an object of the stated class provided that JSON is valid and class structure matches with JSON structure.
That's all I could say about this, hope you got the basic understanding.
I'm using LINQ and XDocument to parse an XML file. Some of the fields in the XML are not fixed, in that they could sometimes be a double or a string. Normally it will be a double but a string will indicate that the data was not available.
For example:
<current_observation>
<temp_c>12.1</temp_c>
<temp_c>NA</temp_c>
</current_observation>
I read in the XML fields and then set properties in a new object instance. Like this:
var data = from i in weatherResponse.Descendants("current_observation")
select new CurrentConditions
{
// Attempt to parse. Set to null if not a double.
// This is one of the areas I'm having trouble with.
TemperatureC = Utilities.ParseDoubleValue(i.Element("temp_c"))
// If I use the following line instead then it works without problem.
// But this misses out all of the safe parsing.
// TemperatureC = (double)i.Element("temp_c")
};
I decided to use the Nullable type, so that I can use TryParse to either parse the field to a double, or if that was not possible set the property to a null.
Here is the code I'm using to try and parse the field:
public static class Utilities
{
public static double? ParseDoubleValue(object inputValue)
{
if (inputValue == null)
return null;
double returnValue;
return double.TryParse(inputValue.ToString(), out returnValue) ? returnValue : (double?)null;
}
}
However, it seems that somewhere in my code it is not actually parsing the field properly, because if I do this:
if(currentConditions.TemperatureC.HasValue)
Console.WriteLine("Has a value: {0}", currentConditions.TemperatureC.Value);
else
Console.WriteLine("Not Avaliable.");
Then HasValue will always return false.
My question: Why is my method of attempting to parse the value not working? Have I misunderstood the usage of TryParse and nullable types?
XElement defines explicit type conversions to the various numeric types. Consequently, (double)i.Element("temp_c") is permitted and casts the value of the XElement to a double data type (via an internal call to Double.Parse).
When the XElement is passed as an argument of type object to your Utilities.ParseDoubleValue method, you are passing the entire XElement. The call to inputValue.ToString() returns not 12.1, but rather <temp_c>12.1</temp_c>, the indented XML value for the node. And double.TryParse("<temp_c>12.1</temp_c>") will, of course, fail.
The error rests in that you most likely want to provide this method with the string value of the XElement, rather than the XElement itself. This is a simple matter of changing:
TemperatureC = Utilities.ParseDoubleValue(i.Element("temp_c"))
to
TemperatureC = Utilities.ParseDoubleValue(i.Element("temp_c").Value)
Second, as a defensive measure, you may want to reconsider the method signature for your method:
public static double? ParseDoubleValue(object inputValue)
This will accept any object, but you are ultimately interested in converting a string that the caller provides. Changing it to accept only a string will ensure that callers provide a string, and failure to do so will result in a compilation error.
public static double? ParseDoubleValue(string inputValue)
i.Element returns an XElement, not the value. And, of course, this isn't going to be parseable to a number. You need i.Element("temp_c").Value
I am attempting to take an object structured like so
public class Item
{
public Guid SourceTypeID {get;set;}
public Guid BrokerID {get;set;}
public double Latitude {get;set;}
public double Longitude {get;set;}
public DateTime TimeStamp {get;set;}
public object Payload {get;set;}
}
and serialize it with JSON.NET using a call like:
Item expected = new Item()
{
SourceTypeID = Guid.NewGuid(),
BrokerID = Guid.NewGuid(),
Latitude = 33.657145,
Longitude = -117.766684,
TimeStamp = DateTime.Now,
Payload = new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
};
string jsonString = JsonConvert.SerializeObject(expected);
The payload member of the Item object will potentially hold any one primitive of a list of C# primitives (plus a few others like Guid), or an array of those types (as in the example, a byte array), or a "flat" object composed of any of those previously listed "primitives" (dynamically created).
When I perform the SerializeObject() call, the string that is produced contains:
{"Payload":"AAECAwQFBgcICQ==","SourceTypeID":"d8220a4b-75b1-4b7a-8112-b7bdae956a45",
"BrokerID":"951663c4-924e-4c86-a57a-7ed737501dbd",
"TimeStamp":"\/Date(1328202421559-0600)\/",
"Latitude":33.657145,"Longitude":-117.766684}
However when I make the deserializing call the item that is produced is partially incorrect
Item actual = JsonConvert.DeserializeObject<Item>(jsonString);
actual.SourceTypeID : {00000000-0000-0000-0000-000000000000}
actual.BrokerID : {951663c4-924e-4c86-a57a-7ed737501dbd}
actual.Latitude : 33.657145;
actual.Longitude : -117.766684;
actual.TimeStamp : {2/2/2012 11:07:01 AM}
actual.Payload : null
The SourceTypeID member (Guid), and the Payload member (object, holding a byte[]), are both incorrect. The serialized string seems to hold the right identity for the guid, but not for the byte array.
I see that an alternate signatureof SerializeObject is
SerializeObject(object value, params JsonConverter[] converters);
Is this the correct way to inform the de/serialization engine about types that it apparently handles wrong? Since I am working with a limited set of "primitives" at the core of my object, if I could create a set of converters for each of those types, would that solve my problem?
I want to avoid reinventing the wheel if possible, as it seems that this would be a problem that has already been handled gracefully, and I'd like to leverage something like that if available.
Payload will be deserialized as a string unless you place a [JsonProperty] attribute with TypeNameHandling enabled on it, otherwise the deserializer won't know what to deserialize it as.
I haven't been able to duplicate the problem you got with some properties being null using the latest source code at http://json.codeplex.com/SourceControl/list/changesets
This appears to be an actual bug in JSON.NET.
It's not idea, but a workaround might be to have a two-stage serialization. The first, the object that actually gets serialized to/from JSON, would consist only of fields/properties with string type. The second object (the one your server-side code would use) would be an object that is converted to strong types manually.
Not ideal, but possible. Additonally, you could consider using the DataContractJsonSerializer, which does a pretty good job of handling byte arrays and guids IME. Dates are still pretty sucky, but that's mostly the fault of Javascript.