I have a simple object to deserialize, but I do not understand the error I get.
The code is the following:
open System
open Newtonsoft.Json
type r =
{
Timestamp: DateTime
Currency: string
PreviousDeposited: int64 option
PreviousWithdrawn: int64 option
PreviousTransferIn: int64 option
PreviousTransferOut: int64 option
PreviousAmount: int64 option
TransferIn: int64 option
TransferOut: int64 option
Amount: int64 option
PendingCredit: int64 option
PendingDebit: int64 option
ConfirmedDebit: int64 option
}
let a =
"{
\"account\": 117122,
\"currency\": \"XBt\",
\"prevDeposited\": 747841316,
\"prevWithdrawn\": 2160000,
\"prevTransferIn\": 1000000,
\"prevTransferOut\": 0,
\"prevAmount\": 656893723,
\"prevTimestamp\": \"2020-06-13T12:00:00.005Z\",
\"deltaDeposited\": 0,
\"deltaWithdrawn\": 0,
\"deltaTransferIn\": 0,
\"deltaTransferOut\": 0,
\"deltaAmount\": 0,
\"deposited\": 747841316,
\"withdrawn\": 2160000,
\"transferIn\": 1000000,
\"transferOut\": 0,
\"amount\": 656893723,
\"pendingCredit\": 0,
\"pendingDebit\": 0,
\"confirmedDebit\": 0,
\"timestamp\": \"2020-06-13T12:00:00.643Z\",
\"addr\": \"2NBMEXRW4oCiNzVUq4uVFRSsK2jtTLbtfc7\",
\"script\": \"532102c10be2f0dc20f4285c25156aa22a0c46d2b89ccc4d1c8eaed92ea0c1a8f40c002102ceba29da1af96a0f2ef7cda6950b8be2baeb1adf12c0d5efebb70dbcaa086ba021034ab762f4ede40311e9f8bf01db0bbea578497ac6ccc8aa94a74394b05a53d94b2103d5a42b90e9d7156155661979530a09d2e12e252ef4104e5611274a7ae7e2b09454ae\",
\"withdrawalLock\": []
}"
JsonConvert.DeserializeObject<r> a
and I get this error:
Newtonsoft.Json.JsonSerializationException: Unexpected property
'transferOut' found when reading union. Path 'transferOut', line 18,
position 18.] at
Newtonsoft.Json.Converters.DiscriminatedUnionConverter.ReadJson(JsonReader
reader, Type objectType, Object existingValue, JsonSerializer
serializer) at
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter
converter, JsonReader reader, Type objectType, Object existingValue)
at
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolvePropertyAndCreatorValues(JsonObjectContract
contract, JsonProperty containerProperty, JsonReader reader, Type
objectType) at
...
I do not understand what makes the property 'TransferOut' so special that it stops on this one, and not on any of the other identical ones before.
I have a fiddle here: https://dotnetfiddle.net/HGiia5
You have a few problems here.
Firstly, the JSON syntax you are using for an option field does not match Json.NET's syntax. If we simplify your type as follows:
type r =
{
TransferIn: int64 option
TransferOut: int64 option
}
And serialize an instance as follows:
let item : r = { TransferIn = Some 1000000L; TransferOut = None}
let json = JsonConvert.SerializeObject(item,Formatting.Indented)
printfn "%s" json
let item2 = JsonConvert.DeserializeObject<r> json // No exception
The result is:
{
"TransferIn": {
"Case": "Some",
"Fields": [
1000000
]
},
"TransferOut": null
}
Which round-trips successfully. Demo fiddle #1 here.
The simple syntax "transferIn": 1000000 you are using for option fields is not implemented by DiscriminatedUnionConverter, the converter that Json.NET uses for serializing discriminated unions including optional fields. This mismatch is causing an exception while reading the JSON.
Relatedly, see Serializing F# Option types which has a suggestion for a nuget package that provides a JsonConverter for option<_> that supports this simplified syntax.
Secondly, many of the JSON property names do not match your f# record names. Json.NET uses an ordinal-case-insensitive algorithm to match JSON property names to f# constructor arguments and member names, but many of your JSON names do not match:
"prevDeposited" does not match PreviousDeposited.
"prevWithdrawn" does not match PreviousWithdrawn.
And several others.
In fact the first property in the JSON that actually matches an option field is "transferIn". You are receiving an error about "transferOut" because it immediately follows the value of "transferIn" which was not deserialized successfully.
Finally, the error message that Json.NET is throwing for invalid JSON values for option fields is non-useful when the field does not appear at the end of a JSON object. If I simplify the input JSON as follows:
{
"transferIn": 1000000,
}
The we get a much more useful error message
Newtonsoft.Json.JsonSerializationException: No 'Case' property with union name found. Path '', line 3, position 1.
Demo fiddle #2 here.
But when "transferIn" is followed by another JSON key/value pair the error message becomes the less useful message shown in your question. You might open an issue with Newtonsoft asking them to improve the error message that DiscriminatedUnionConverter throws when the JSON value for an option field does not match the expected schema and there are subsequent JSON properties in the containing object.
Related
Given some nullable type T?, how do I get the corresponding non-nullable one T ?
For example:
T? x<T extends int?>(T? value) => value;
Type g<T>(T Function(T) t) => T;
Type type = g(x);
print(type); // Prints "int?"
Now I want to get the non-nullable type. How do I create the function convert so that:
Type nonNullableType = convert(type);
print(nonNullableType); // Prints "int"
If you have an instance of T?, and you're trying to do something where the expected type is T, you can use use T! wherever dart is showing an error. It is not exactly a conversion from T? to T, its just a shortcut to do a null check.
In general, you do not. There is no simple way to strip the ? of a type, or destructure types in other ways. (You also can't find the T of type you know is a List<T> at run--time)
If you have the type as a Type object, you can do nothing with it. Using Type object is almost never what you need.
If you have the type as a type parameter, then the type system don't actually know whether it's nullable. Example:
void foo<T>() { ... here T can be nullable or non-nullable ... }
Even if you test null is T to check that the type is actually nullable, the type system doesn't get any smarter, that's not one of the tests that it can derive type information from.
The only types you can improve on are variable types (or rather, the type of a single value currently stored in a variable). So, if you have T x = ...; and you do if (x != null) { ... x is not null here }, you can promote the variable to T&Object, but that's only an intermediate type to allow you to call members on the variable, it's not a real type that you can capture as a type variable or a variable type. It won't help you.
All in all, it can't be done. When you have the nullable type, it's too late, you need to capture it before adding the ?.
What problem are you actually trying to solve?
If you have an instance of T?, I think you could do:
Type nonNullableTypeOf<T>(T? object) => T;
void main() {
int x = 42;
int? y;
print(nonNullableTypeOf(x)); // Prints: int
print(nonNullableTypeOf(y)); // Prints: int
}
If you have only T? itself (the Type object), then I'm not confident that there's much you can do since what you can do with Type objects is very limited. (And given those limitations, it's not clear that nonNullableTypeOf ultimately would be very useful either.)
A related question: How do I check whether a generic type is nullable in Dart NNBD?
As you can see in https://api.dart.dev/stable/2.7.1/dart-convert/jsonDecode.html, it has no type and no documentation. I don't know which methods I can invoke on the result neither I don't know which type to but on a parameter that should be a json object.
Why is Dart like this? And what are the advantages?
It does have documentation, and you are linking to it.
If you want it to have more documentation, then that is reasonable. The returned value is admittedly not documented very well.
The function jsonDecode is a shorthand for json.decode, which again forwards to JsonDecoder.convert.
It returns a "JSON value" object which depends on the JSON source that it decodes.
A "JSON value" can be any of:
* null
* an int
* a double
* a String
* a bool (true or false)
* a List<dynamic> containing zero or more JSON values.
* a Map<String, dynamic> mapping keys to JSON values.
Those are also the same values that are accepted by the JsonEncoder which converts object structures to JSON strings.
Since these types have no common superclass other than Object, the function cannot have a return type which is more specific than dynamic or Object.
The chosen return type is dynamic because the dynamic type allows the receiver to optimistically call any member on the value. They might know that the value will always be a map, so they can just do jsonParse(jsonSource)["key"] to look up a value. Obviously, if the source was not a JSON object, that call will fail.
If you don't know which type the result is, you have to check:
var data = jsonDecode(jsonSource);
if (data is Map<String, dynamic>) {
something something data["key"] something
} else if (data is List<dynamic>) {
something something list[2] something
} else ... etc ...
A valid JSON file is actually a valid Dart expression too. The value returned by jsonDecode is similar to the value you would get if you wrote the JSON code directly as Dart code (in Dart 1 it was exactly the same, in Dart 2, the Dart code might infer a more precise type for maps and lists).
help needed in a REST swagger file.
How can I have default value in a Byte format parameter. or is it even possible ? Tried this one, it doesn't work.
default: "MA=="
gives me this error, at compile time:
incompatible types: java.lang.String cannot be converted to byte[]
example Swagger below:
someDVO:
type: object
description: Contains DocumentID Information.
properties:
specificDocID :
type: string
format: byte
description: The SiteSpecific ID representing the Document.
Any pointer, Thanks
I have the following (simplified) Entity SQL query:
SELECT VALUE a
FROM Customers AS a
WHERE a.Status NOT IN { 2, 3 }
The Status property is an enumeration type, call it CustomerStatus. The enumeration is defined in the EDMX file.
As it is, this query doesn't work, throwing an exception to the effect that CustomerStatus is incompatible with Int32 (its underlying type is int). However, I couldn't find a way to define a list of CustomerStatus values for the IN {} clause, no matter what namespace I prefixed to the enumeration name. For example,
SELECT VALUE a
FROM Customers AS a
WHERE a.Status NOT IN { MyModelEntities.CustomerStatus.Reject, MyModelEntities.CustomerStatus.Accept }
did not work, throwing an exception saying it could not find MyModelEntities.CustomerStatus in the container, or some such.
Eventually I resorted to casting the Status to int, such as
SELECT VALUE a
FROM Customers AS a
WHERE CAST(a.Status AS System.Int32) NOT IN { 2, 3 }
but I was hoping for a more elegant solution.
Oooh, you are writing Entity SQL directly. I see... Any reason you aren't using DbSet instead of writing Entity SQL by hand? Could always do
var statuses = new [] { Status.A, Status.B };
var query = context.SomeTable.Where(a => !statuses.Contains(a.Status)).ToList();
code is an integer, so it's declared as an Int in my subclass.swift file. The subclass is in the same format as the JSON, to enable saving the JSON directly to Realm.
I get this JSON:
...
"code": 301
...
And this is how I'm saving it:
realm.create(Student.self, value: jsonStudent, update: true)
But Realm throws this:
failed: caught "RLMException", "Invalid value '301' for property 'code'"
At first I thought the '301' might be getting parsed as a string, but that was not the case, calling 'dynamicType' on it in the debugger returns NSCFNumber, which is expected.
What's wrong?
NSJSONSerialization will convert {"code": 301} to an NSNumber of type q, which is integer. So the fact that you're getting an invalid property implies that the code property on your Realm model is of a different type (maybe Float or Double)?
Could you share your model definition here?
If your code property is actually Int or a variation thereof (Int32, Int64, etc.), this would imply there's a bug in Realm and I'd recommend that you open an issue at https://github.com/realm/realm-cocoa/issues.