JON (Jacy Object Notation) is an alternative to JSON used by Jacy programming language.
This is a C++ library to work with JON
- Simple API to work with JON values
- Serialization / Deserialization
- JON string literals (
"..."_jon
) - Built-in JON schema validator Schemas
- First-Access conversions
If you work in VSCode install Jacy
extension, it includes syntax highlighting for Jon
.
No other IDEs supported, and if it will be, likely, Jon
will be included into Jacy
extensions.
Anyway, just for syntax highlighting you can associate *.jon
files with *.yaml
extension as syntax is pretty similar.
For JetBrains IntelliJ IDEA, go to Settings > Editor > File Types
and peek YAML
(if there's no YAML
file type, YAML plugin is disabled), click +
and add *.jon
to list of associated files.
This is a header-only library, all you need is to have source in your project.
For now, there's no "single-header" variant, thus you need the whole include
jon subdirectory.
- Download latest Jon source from.
- Move files to directory you want, e.g.
thirdparty/jon
- Add
jon
directory to your project using-Ithirdparty/jon
or by addingtarget_include_directories(thirdparty/jon)
toCMakeLists.txt
- Use 😊
#include "jon/jon.h"
// Don't use `using namespace jacylang`, better write:
using jon = jacylang::jon;
// Add this to use `""_jon` string literals
using namespace jacylang::literal;
int main() {
jon jonObject = R"(
app: 'Jon'
version: '1.0.0'
description: """
_JON_ (Jacy Object Notation) is an alternative to JSON used by
Jacy programming language.
"""
)"_jon;
return 0;
}
// Jon empty constructor defaults value to null
jon nullValue;
// Or like this
jon nullValue1 {};
// You can explicitly create a null_t value
jon nullValueExplicit {jon::null_t {}};
// boolean accepts `true` or `false`
jon trueValue = true;
jon falseValue = false;
// `jon` constructors are smart enough to avoid problems with implicit
// conversion from `int` to `bool` or `double`
// Integers are stored as `int64_t`
jon intValue = 123;
jon negIntValue = -1812312;
// `double` is used for floating-point numbers
jon floatValue = 0.12312321;
// Strings
jon stringValue = "Hello, Jon!";
// Objects
// Using `jon::obj` method which accepts initializer_list of key-value pairs,
// key must be a string
jon objectValue = jon::obj({
{"key", "value"},
});
// Or by creating value of `jon::obj_t`
// which is likely an `std::map<std::string, jon>`
jon objectInit = jon::obj_t {
{"name", "Jon"},
};
// Create an empty object
jon emptyObj = jon({});
This is the way to work with jon
value as dynamically-typed. When you create an empty jon
value (which defaults to null
) and then access it with type-dependent method, type is automatically set to requested.
Example:
jon useAsArray;
std::cout << useAsArray.typeStr() << std::endl; // prints `null`
useAsArray.push(jon {123});
std::cout << useAsArray.typeStr() << std::endl; // prints `array`
static jon fromFile(const std::filesystem::path & path);
Read contents of file by path, parses it and returns jon
value.
static jon parse(const str_t & source);
Parses source string and returns jon
value.
template<class T>
constexpr T & get();
template<class T>
constexpr const T & get() const;
Returns stored value by type.
JON Type | Return type |
---|---|
null |
std::monostate |
bool |
bool |
int |
int64_t |
float |
double |
string |
std::string |
object |
std::map<std::string, jon> |
array |
std::vector<jon> |
Throws jon::type_error
if tried to get invalid type.
bool empty() const noexcept;
Checks if value is empty, only collection-like types have dynamic size, null
is zero-sized and bool
, int
and double
are 1-sized.
bool has(const str_t & key) const noexcept;
Checks if JON object contains key-value pair with given key, if value is not
an object - returns false
.
void clear() noexcept;
Clears value, resetting it to default-constructed.
Default values for JON types are:
JON Type | Default value |
---|---|
null |
null |
bool |
false |
int |
0 |
float |
0.0 |
string |
"" |
object |
{} |
array |
[] |
size_t size() const noexcept;
Returns size of value, null
is always zero-sized, bool
, int
and double
are 1-sized and string
, object
and array
are dynamically-sized.
JON Type | Size |
---|---|
null |
0 |
bool |
1 |
int |
1 |
float |
1 |
string |
Result of std::string::size() |
object |
Result of std::map<std::string, jon>::size() |
array |
Result of std::vector<jon>::size() |
Returns jon::Type
of value.
enum class Type {
Null,
Bool,
Int,
Float,
String,
Object,
Array,
}
static std::string typeStr(Type type);
std::string typeStr() const;
Returns type as string.
bool isNull() const noexcept;
bool isBool() const noexcept;
bool isInt() const noexcept;
bool isFloat() const noexcept;
bool isString() const noexcept;
bool isObject() const noexcept;
bool isArray() const noexcept;
Simple type-checking methods.
jon & check(Type expectedType) const;
Checks that value is of given type and returns *this
.
Throws jon::type_error
if type check failed.
(1) jon & operator[](const str_t & key);
(2) jon & operator[](size_t idx);
template<class T>
(3) jon & operator[](const T & key);
Element-access operators without bound checks, those that receive str_t
are
used for objects, those that receive size_t
are used for arrays.
(1) Access object element by key.
(2) Access array (or object) element by index. If value is an object then
idx
is converted to string and then operator[](const str_t & key)
is
called.
(3) Access to object element by key of any type that can be converted to key
type (str_t
), allows only null_t
, bool_t
, int_t
, float_t
or str_t
. Access by obj_t
or arr_t
is not allowed.
Important: operator[](size_t idx
inherits std::vector::operator []
logic and unlike std::map::operator[]
does not insert new element, thus
calling to it with non-existent element index is Undefined Behaviour.
(1) throws jon::type_error
if value is not an object
.
(2) throws jon::type_error
if value is not an array
.
(3) throws jon::type_error
if value cannot be coerced to key type (str_t
) or value is not an object
.
bool operator==(const jon & other) const;
Equality operator, requires both values to be of same type, size and value.
Comparison depends on type:
null
- both values arenull
bool
-bool == bool
int
-int64_t == int64_t
float
-std::abs(get<float_t>() - other.get<float_t>()) < std::numeric_limits<double>::epsilon()
string
-std::string == std::string
object
-std::equal(get<obj_t>().begin(), get<obj_t>().end(), other.get<obj_t>().begin())
array
-std::equal(get<arr_t>().begin(), get<arr_t>().end(), other.get<arr_t>().begin())
(1) const jon & at(const str_t & key) const;
(2) jon & at(const str_t & key);
(3) const jon & at(size_t idx) const;
(4) jon & at(size_t idx);
template<class T>
(5) const T & at(const str_t & key) const;
template<class T>
(6) T & at(const str_t & key);
(1), (2), (3), (4) are same as operator[]
but with bound checks, throw an
exception if nothing found.
(5), (6) are shortcuts for at
+ get
and are equivalents to:
at(key).get<T>()
(1-6) throw jon::out_if_range
if no element found by key/index.
(1), (2), (5), (6) throw jon::type_error
if value is not an object
.
(3) and (4) throw jon::type_error
if value is not an array
.
(5) and (6) throw jon::type_error
if value is not of type T
.
(1) std::string dump(const Indent & indent = {"", -1}) const;
(2) std::string dump(const std::string & indentStr) const;
(3) std::string dump(uint16_t spaceSize) const;
(1) Converts JON value to string.
(2) is an equivalent to:
dump(Indent {indentStr, 0});
(3) is an equivalent to:
dump(Indent {std::string(spaceSize, ' '), 0});
By default, dump
returns escaped string, if no indent provided and
default one chosen.
Indent
with size = -1
means that no indentation will be applied and
output string won't be prettified.
jon validate(const jon & schema) const;
Validates JON value by schema, returns null
jon value if value is valid, otherwise returns schema-like error structure.
Read more about schemas Schemas
Throws jon::invalid_schema
if schema has invalid structure.
jon operator""_jon(const char * str, std::size_t n);
Instantiates JON literal. Requires using namespace jon::literal
Examples:
auto jon1 = "hello: 'world'"_jon;
auto jon2 = R"(
project-name: 'Jacy'
version: '1.0.0'
dependencies: {
library: '2.1.15'
}
)"_jon;
static float_t getNaN();
Returns NaN
float_t
value used as NaN in jon
.
This is the value you get when use nan
literal, which is sign-independent, thus you always get NaN
for nan
, +nan
and -nan
.
bool isNaN() const;
Checks if value is of type float_t
and is NaN
.
bool isInf() const;
Checks if value is of type float_t
and is infinity.
bool isPosInf() const;
Checks if value is of type float_t
and is positive infinity.
bool isNegInf() const;
Checks if value is of type float_t
and is negative infinity.
static jon obj(std::initializer_list<detail::jon_ref<jon>> init = {});
Creates an object jon
value from initializer list.
Accepts initializer_list
which is a list of key-value pairs where each key is a string and value is jon
-compatible value.
jon flatten() const;
Flattens jon
value, does not, every converts object or array to one-level object where each key is a path-like string to value.
void push(const jon & el);
Pushes value to jon array.
Supports "Array First Access"
using size_type = int32_t;
Indent(const std::string & val, size_type size = 0);
Simple struct
used for indentation. Holds indent value as std::string
and
size (count of repetitions of string).
If size
is -1
then indent won't be written to std::ostream
.
Example:
const auto schema = R"(
type: 'object'
props: {
title: {
type: 'string'
minLen: 8
}
values: {
type: 'array'
items: {
type: 'int'
minInt: 0
}
}
}
)"_jon;
const auto first = R"(
title: 'short'
values: [
1
2
3
-100
]
)"_jon;
std::cout << first.validate(schema).dump(2);
/* Prints:
{
title: 'Invalid string size: 5 is less than 8'
values: {
3: 'Invalid integer size: -100 is less than 0'
}
}
*/
There're 4 ways to specify type of value in schema.
#1
field: {
type: '{TYPE}'
}
This one allows placing more constraints with other keywords.
#2
field: '{TYPE}'
Shortcut for variant above and is simpler, but you cannot specify constraints for value.
#3
field: {
type: ['{TYPE_1}', '{TYPE_2}', ...]
}
Here field
can be of any of given type.
#4
field: ['{TYPE_1}', '{TYPE_3}', ...]
Similar to #2 variant, but shortcut for #4 one.
If type is an array you still can specify any constraints for each of these types.
This is why keywords like minInt
/maxInt
are not min
/max
, because if you have type: ['int', 'string']
then min
/max
would be ambiguous -- are these bounds for int
value or for string
size?
Possible type names
JON Schema Type |
---|
any |
null |
bool |
int |
float |
string |
object |
array |
any
type does not exist in jon::Type
, but in schema it just allows value
to be of any type, similar to array of all possible types (['null', 'bool ', 'int', 'float', 'string', 'object', 'array']
).
Important: Schema can be either object
, string
(just a type) or null
. null
is just the same as any
type schema. All over types are INVALID
for schema.
This keywords can be applied to schema for any type.
type
[string
]: Type of value. (Default:any
)nullable
[bool
]: Allows value to benull
, similar totype: ['{TYPE}', 'null']
. (Default:false
)anyOf
[array
of schemas]: Value is valid if any of schemas in array is valid for value.oneOf
[array
of schemas]: Value is valid if EXACTLY ONE of schemas in array is valid for value.allOf
[array
of schemas]: Value is valid if ALL of schemas in array is valid for value.not
[array
of schemas / schema]: Value is valid if it does not match any schema innot
array (if it is an array) or the onenot
schema (if it is not an array).
minInt
[int
]: Minimum integer value. (Optional)maxInt
[int
]: Maximum integer value. (Optional)
minFloat
[float
]: Minimum float value. (Optional)maxFloat
[float
]: Maximum float value. (Optional)
minLen
[int
]: Minimum length of string value. (Optional)maxLen
[int
]: Maximum length of string value. (Optional)pattern
[string
]: Regular expression to match against a value. (Optional)
minProps
[int
]: Minimum count of properties in object. (Optional)maxProps
[int
]: Maximum count of properties in object. (Optional)props
[object
]: Schema object for properties validation. (Optional)extras
[bool
]: Allows additional properties in object, does not depend onprops
, thus ifprops
is not specified andextras
isfalse
then no properties in object would be allowed. (Default:[]
)
This keywords only allowed for schema which is a property of an object schema.
optional
[bool
]: Allows missing property marked asoptional
. (Default:false
)
minSize
[int
]: Minimum size of array. (Optional)maxSize
[int
]: Maximum size of array. (Optional)items
[object
]: Schema object for items validation. (Optional)