By design, all fields are required, ie. null
or missing fields will cause a TypeError
while reviving. In the case of the Animal
class from the :
Copy const pet = M.fromJSON(Animal, '{"name": null}')
// => TypeError: no value for key "name"
To support missing properties or null
values, you can either use withDefault
or declare the property as a Maybe
Using withDefault
The withDefault
metadata takes some metadata and a default value which will be used only if the property is null
or missing.
Copy import M from 'modelico'
const {string, maybe} = M.metadata()
class Animal extends M.Base {
// ... same as before
static innerTypes () {
return Object.freeze({
name: withDefault(string(), 'Unknown')
})
}
}
Then, it can be used as any other value:
Copy const pet1 = M.fromJSON(Animal, '{"name": "Bane"}')
const pet2 = M.fromJSON(Animal, '{"name": null}')
const pet3 = M.fromJSON(Animal, '{}')
pet1.name() // => Bane
pet2.name() // => Unknown
pet3.name() // => Unknown
Maybes
Copy import M from 'modelico'
const {string, maybe} = M.metadata()
class Animal extends M.Base {
// ... same as before
static innerTypes () {
return Object.freeze({
name: maybe(string())
})
}
}
Then, we can use it as follows:
Copy const pet1 = M.fromJSON(Animal, '{"name": "Bane"}')
// pet2 and pet3 behave the same way
const pet2 = M.fromJSON(Animal, '{"name": null}')
const pet3 = M.fromJSON(Animal, '{}')
pet1.name().isEmpty() // => false
pet1.name().getOrElse('Coco') // => Bane
JSON.stringify(pet1) // => {"name":"Bane"}
pet2.name().isEmpty() // => true
pet2.name().getOrElse('Bane') // => Bane
JSON.stringify(pet2) // => {"name":null}
pet3.name().isEmpty() // => true
pet3.name().getOrElse('Bane') // => Bane
JSON.stringify(pet3) // => {"name":null}
Note: pet2
does not produce the same JSON it was parsed from. If that is important to you, one possibility would be to not declare the name
field and use M.fields(pet2).name
to check for its presence and value manually.