glideapps / glideapps/quicktype
CSharp: JSON values with decimal point are generated as `long` properties, should be `double`/`decimal`
- Dominant language
- TypeScript
- Stars
- 13.9k
- Forks
- 1.2k
- Avg merge
- 8h 53m
- Merged PRs (30d)
- 369
Description
Minimal reproducible example:
JSON:
```json
{
"test": 123.0
}
```
Resulting CSharp:
```csharp
//
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
// using QuickType;
//
// var welcome = Welcome.FromJson(jsonString);
namespace QuickType
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class Welcome
{
[JsonProperty("test")]
public long Test { get; set; }
}
public partial class Welcome
{
public static Welcome FromJson(string json) => JsonConvert.DeserializeObject(json, QuickType.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this Welcome self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
}
```
Options are default:


As you can see, QuickType results in type `long`, even though there is a `.0` in the JSON.
Yes, if I change it to `.1`, the resulting type will be `double` or `decimal`, depending on the settings.
But I want QuickType to detect that I have `.0` in the value, and generate a decimal number type, not integer.
# Current behavior:
1. If a number key in the object is `123.0` (i.e. there is a decimal part, but it's zero), the resulting type is `long`.
1. If a number key in the object is `123.1` (i.e. there is a decimal part, and it's not zero), the resulting type is `double` or `decimal`.
1. If a number key in the object is `123` (i.e. there is no decimal part), the resulting type is `long`.
# Expected/desirable behavior:
1. If a number key in the object is `123.0` (i.e. there is a decimal part, but it's zero), the resulting type is `double` or `decimal`.
1. If a number key in the object is `123.1` (i.e. there is a decimal part, and it's not zero), the resulting type is `double` or `decimal`.
1. If a number key in the object is `123` (i.e. there is no decimal part), the resulting type is `long`.
# Why?
Because otherwise C# throws an error like this:
```
Unhandled exception. System.Text.Json.JsonException: The JSON value could not be converted to System.Int64. Path: $.payload.0.payload[0].loyalty[0].amount | LineNumber: 50 | BytePositionInLine: 43.
---> System.FormatException: Either the JSON value is not in a supported format, or is out of bounds for an Int64.
at System.Text.Json.ThrowHelper.ThrowFormatException(NumericType numericType)
at System.Text.Json.Utf8JsonReader.GetInt64()
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonDictionaryConverter`3.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TDictionary& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, Utf8JsonReader& reader, Exception ex)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo`1 jsonTypeInfo)
at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options)
at AAA.BBB.AccountsRoot.FromJson(String json) in C:\Users\...\RiderProjects\AAA\AAA\BBB\Commons.cs:line 451
at my code that executes AccountsRoot.FromJson
```
the stack trace above is from my actual JSON that I needed to convert to CSharp classes in QuickType. but the point still stands - the System.Text.Json serializer cannot deserialize `123.0` into a long, therefore it should be a decimal or a double.
This change can become a default behavior, or be toggleable with a checkbox in the options, doesn't matter.
The only workaround I see now is to replace `/(\d+)\.0+\b/` (or something) with `$1.001` or something, e.g. turn `123.0` into `123.001` to force QuickType to turn numbers with `.0` into decimals, while keeping integer values (e.g. `123`) intact.
Contributor guide
Assessment
This issue has not been assessed yet.