Optional property for kjson.
There is a bug in the Kotlin reflection subsystem relating to value classes that prevents this library working as intended.
The current version (1.1) uses a regular class instead of a value class, and will therefore not be as efficient as planned. A new version using a value class will be released as soon as the issue is resolved.
When deserializing JSON objects, the kjson library will use the default value
in the constructor for any missing properties.
The convention is to use null
as this default, but this makes it difficult to distinguish between a missing property
and one that has been set intentionally to null
.
The kjson-optional
library provides an Opt
class which, when used in conjunction with the appropriate
deserialization functions, will provide the ability to determine whether a property was omitted or was supplied with the
value null
.
A good example of an optional property is the middle name of a name class:
data class PersonName(
val firstName: String,
val middleName: Opt<String> = Opt.unset(),
val surname: String,
)
Then, to access the middleName
:
val displayName = buildString {
append(firstName)
append(' ')
if (middleName.isSet) {
append(middleName.value)
append(' ')
}
append(surname)
}
Opt
is a value class, which means that in many cases the compiler will optimise away the instantiation of a separate
"holder" class.
The use of Opt
will incur some overhead compared to the use of a raw type, but when used as described above, it will
not lead to the creation of a large number of small objects, with the associated garbage collection cost.
The name Opt
was chosen because a single data class is likely to contain several optional fields, and a long name
would be very obtrusive.
The package name io.kjson.optional
provides a bit more explanation of the function of the class.
The java.lang.Optional
class was considered for this use but ruled out because it does not allow null values, and
because it would not allow the use of a value class.
To construct an Opt
:
val optString = Opt.of("A string")
or:
val optString = Opt.unset<String>()
Both of these will construct an Opt<String>
.
To construct an Opt
from a possibly-null value:
val optString = Opt.ofNullable(nullableString)
The Opt
will be unset if the parameter was null
.
In all cases the Opt
will be immutable.
To get the value:
val str = optString.value
str
will contain the value as a String
; if the Opt
was unset a NotSetException
(a derived class of
IllegalStateException
) will be thrown.
To test whether the value is set:
if (optString.isSet) {
// use optString.value
println(optString.value)
}
To test if the value is unset:
if (optString.isUnset) {
// do NOT use optString.value
println("Value is not set")
}
To get the value, or null
if it was unset:
val str = optString.orNull
In this case, str
will be of type String?
.
To get the value, or some other value if it was unset:
val str = optString.orElse { "default" }
This can be used to throw an alternative exception type in place of NotSetException
:
val str = optString.orElse { throw StringMissingException() }
There is also a convenience function:
optString.ifSet { println(it) }
The lambda will be executed only if the value is set, and the value will be passed in as the parameter.
The latest version of the library is 1.1, and it may be obtained from the Maven Central repository.
<dependency>
<groupId>io.kjson</groupId>
<artifactId>kjson-optional</artifactId>
<version>1.1</version>
</dependency>
implementation "io.kjson:kjson-optional:1.1"
implementation("io.kjson:kjson-optional:1.1")
Peter Wall
2023-04-11