Build and access JSON/YAML data using a dynamic sumtype instead of static types.
- Type
Any
accomodating all JSON types -null
,boolean
,number
,string
,array
andobject
. - Strict extraction of values checking for the right return type.
- Safe conversion of all numberic types (
u8
,u16
,u32
,u64
,i8
,i16
,int
,i64
andf32
) checking for arithmetic overflow. - Factory functions for creating JSON values.
- Built-in formatting to
string
for easy logging. - Convenient traversing of nested values in arrays and objects.
- Unmarshalling
Any
data to a static V type and marshalling a V value toAny
data.
Used in prantlf.json and prantlf.yaml packages.
Attention: If you dependened on the package prantlf.jsany
, rename the dependency to prantlf.jany
.
import prantlf.jany { Any, Null, any_int, marshal, MarshalOpts, unmarshal, UnmarshalOpts }
// Create an Any
any := any_int(42) // factory function
any := Any(f64(42)) // cast to sumtype
// Checks for null
if any is Null {}
if any.is_null() {}
// Get a value
val := any.number()! // get a number (f64)
val := any.int()! // get an integer
// Format a value to string
str := any.str()
// Get a value on a path
val := any.get('a.b[0]')!
// Set a value on a path
any.set('a.b[0]', any_int(42))!
// Marshal a statically typed value to an Any
struct Config {
answer int
}
config := Config{ answer: 42 }
any := marshal(config, MarshalOpts{})
// Unmarshal an Any to a static type
struct Config {
answer int
}
any := Any({
'answer': Any(f64(42))
})
config := unmarshal[Config](any, UnmarshalOpts{})
You can install this package either from VPM or from GitHub:
v install prantlf.jany
v install --git https://github.com/prantlf/v-jany
The type Any
is a sumtype of the following built-in V types:
// JSON types: null boolean number string array object
pub type Any = Null | bool | f64 | string | []Any | map[string]Any
The null
is a special value in the jany
namespace:
pub const null = Null{}
Null
can be used to check the null
value with the is
operator. There's a convenience method is_null()
too:
if any is Null {}
if any.is_null() {}
Built-in V types are mapped to JSON types in the following way:
JSON type | V type |
---|---|
null |
Null |
boolean |
bool |
number |
f64 |
string |
string |
array |
[]Any |
object |
map[string]Any |
The native V type of an Any
can be checked by the operator is
:
if any is string {}
The name of the JSON type of an Any
can be obtained as a string by the function .typ()
:
typ := any.typ()
An Any
can be created by casting a native V value to the sumtype, or using a factory function exported from the jany
namespace:
JSON type | V type | Casting | Factory |
---|---|---|---|
null |
Null |
Any(null) |
.any_null() |
boolean |
bool |
Any(true) |
.any_boolean(true) |
number |
f64 |
Any(f64(1)) |
.any_number(1) |
string |
string |
Any('a') |
.any_string('a') |
array |
[]Any |
Any([Any(f64(1))]) |
.any_array([1]) |
object |
map[string]Any |
Any({'a': Any(f64(1))}) |
.any_object({ 'a': 1 }) |
For V types mapped from JSON types, there're getters. If the JSON value doesn't match the type of the getter, an error will be returned:
JSON type | V type | Getter |
---|---|---|
null |
Null |
.is_null() |
number |
f64 |
.number() |
boolean |
bool |
.boolean() |
string |
string |
.string() |
array |
[]Any |
.array() |
object |
map[string]Any |
.object() |
Except for the basic V types mapped from JSON types, there're additional getters for other numeric V types. If the JSON value doesn't match the type of the getter, or of the numeric type cannot accomodate the value and an arithmetic overflow would occur, an error will be returned:
JSON type | V type | Getter |
---|---|---|
number |
u8 |
.u8() |
number |
u16 |
.u16() |
number |
u32 |
.u32() |
number |
u64 |
.u64() |
number |
i8 |
.i8() |
number |
i16 |
.i16() |
number |
int |
.int() |
number |
i64 |
.i64() |
number |
f32 |
.f32() |
Serialisation of an Any
to a string representation is expected from libraries providing the specific format, like JSON or YAML. The Any
type provides only a simple serialisation to string for logging purposes - the getter .str()
:
str := any.str()
Values nested in arrays objects can be obtained by a convenience function using a string path - .get(...)
. The syntax is the same as in the V langauge - arrays are accessed by [usize]
and fields by .
. Quotation marks ("
or '
) can be used to specify field names with some of three special characters ([
, ]
and .
). If a value on any level of the the specifcfied path is missing, an error will be returned:
val := any.get('a.b[0]')!
Similarly to getting a nested value, a nested value can be set too. If the parent value on the path (the one but last value) is missing, an error will be returned. Arrays need to have the proper length before assigning values to them:
any.set('a.b', Any([]Any{}))!
any.set('a.b[0]', any_int(42))!
A new item can be added to an array too:
any.add('a.b', any_int(42))!
Except for using Any
values directly, they can be converted to static V types and back again by functions exported from the jany
namespace:
Marshals a value of T
to Any
value. Fields available in MarshalOpts
:
Name | Type | Default | Description |
---|---|---|---|
enums_as_names |
bool |
false |
stores string names of enum values instead of their int values |
struct Config {
answer int
}
config := Config{ answer: 42 }
any := marshal(config, MarshalOpts{})!
Unmarshals an Any
value to a new instance of T
. Fields available in UnmarshalOpts
:
Name | Type | Default | Description |
---|---|---|---|
require_all_fields |
bool |
false |
requires a key in the source object for each field in the target struct |
forbid_extra_keys |
bool |
false |
forbids keys in the source object not mapping to a field in the target struct |
cast_null_to_default |
bool |
false |
allows null s in the source data to be translated to default values of V types; null s can be unmarshaled only to Option types by default |
ignore_number_overflow |
bool |
false |
allows losing precision when unmarshaling numbers to smaller numeric types |
struct Config {
answer int
}
any := Any({
'answer': Any(f64(42))
})
config := unmarshal[Config](any, UnmarshalOpts{})!
Unmarshals an Any
value to an existing instance of T
. Fields available in UnmarshalOpts
:
Name | Type | Default | Description |
---|---|---|---|
require_all_fields |
bool |
false |
requires a key in the source object for each field in the target struct |
forbid_extra_keys |
bool |
false |
forbids keys in the source object not mapping to a field in the target struct |
cast_null_to_default |
bool |
false |
allows null s in the source data to be translated to default values of V types; null s can be unmarshaled only to Option types by default |
ignore_number_overflow |
bool |
false |
allows losing precision when unmarshaling numbers to smaller numeric types |
struct Config {
answer int
}
any := Any({
'answer': Any(f64(42))
})
mut config := Config{}
config := unmarshal_to(any, mut config, UnmarshalOpts{})!
In lieu of a formal styleguide, take care to maintain the existing coding style. Lint and test your code.
Copyright (c) 2023-2024 Ferdinand Prantl
Licensed under the MIT license.