Hi all,
For Crystal 0.25.1, I have a case where I have a Mapping as follows:
JSON.mapping(
items: Array({
"id": Int64,
"active": Bool,
...
})
)
I use rest call to bring in JSON data from a URL. The "active" is optional in some cases. I tried adding Bool? to see if that will help. However, the from_json.cr code is ending up throwing an exception.
Work around I had was to have a | for each of the optional named tuple fields:
JSON.mapping(
items: Array({
"id": Int64,
"active": Bool,
...
} | {
"id": Int64,
... # Without the "active" NamedTuple
})
)
Now this can lead to some rather unpleasant and hard-to maintain code depending on number of optional fields. What would be the right way to handle or declare these? One thought is to perhaps have "Bool?" as a type to indicate that the exception noted above doesn't get thrown. Thoughts?
Mapping options allow you to declare a field as nilable or define a default value. See API docs for specifications.
Sorry, forgot to mention that I tried that:
"active": { type: Bool, nilable: true },
Received the following compiler error:
Syntax error in src/response/category_response.cr:13: expecting token 'CONST', not 'true'
"active": { type: Bool, nilable: true },
@straight-shoota Bool? should work too.
Tried Bool? too, still errors out if missing value. For what its worth, I think its something to do with the fact that the mapping is inside the Array({ NamedTuple }). Here's a simple test code to show what I mean:
require "json"
# This class shows the case I'm trying to solve - Array of NamedTuples...
class Test
JSON.mapping(
items: Array({
"active": Bool
## Here, "active": Bool? does not work,
## "active": { type: Bool, nilable: true } doesn't work
## tried a few other [nonsensicle] things like type set to Bool | Nil. No luck.
})
);
end
# This class works with nilable: true or Bool? since its straight up attributes
class Test2
JSON.mapping(
"active": { type: Bool, nilable: true }
);
end
# Sample outputs (both output true) but note how the fact that structures are different.
puts Test.from_json(%({"items":[{"active": true}]})).items[0][:active]
puts Test2.from_json(%({"active": true})).active
This doesn't work. JSON.mapping expects a list of field names and types/options. Mappings can't be nested. You either need an additional type or a converter for your array.
@Sija You're right, Bool? should already work if properly used.
You need an Item type with its own mapping and then declare items: Array(Item) in you main type.
Note: this is a question for stackoverflow, not a language issue.
Understood @ysbaddaden . I was debating whether this was an issue or not else would have asked directly on Stackoverflow. Will start there going forward. Thanks @straight-shoota @Sija @ysbaddaden
@whizdom It's not an issue, @ysbaddaden answer is the correct one (use an Item type). Also, please start using JSON::Serializable instead of JSON.mapping. The latter will be eventually removed from the language.
Most helpful comment
@whizdom It's not an issue, @ysbaddaden answer is the correct one (use an
Itemtype). Also, please start usingJSON::Serializableinstead ofJSON.mapping. The latter will be eventually removed from the language.