#
# Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending
#
"""The deephaven JSON module presents a declarative and composable configuration layer for describing the structure of a
JSON (https://www.json.org) value. Most commonly, this will be used to model the structure for a JSON object. For
example, the JSON object
.. code-block:: json
{ "name": "Foo", "age": 42, "location": { "lat": 45.018269, "lon": -93.473892 } }
can be modelled with the dictionary
.. code-block:: python
{ "name": str, "age": int, "location": { "lat": float, "lon": float } }
Notice that this allows for the nested modelling of JSON values. Other common constructions involve the modelling of
JSON arrays. For example, a variable-length JSON array where the elements are the same type
.. code-block:: json
[42, 31, ..., 12345]
can be modelled with a single-element list containing the element type
.. code-block:: python
[ int ]
If the JSON array is a fixed size and each elements' type is known, for example
.. code-block:: json
["Foo", 42, [45.018269, -93.473892]]
can be modelled with a tuple containing each type
.. code-block:: python
(str, int, (float, float))
Notice again that this allows for the nested modelling of JSON values. Of course, these constructions can be all be used
together. For example, the JSON object
.. code-block:: json
{
"name": "Foo",
"locations": [
[45.018269, -93.473892],
...,
[40.730610, -73.935242]
]
}
can be modelled as
.. code-block:: python
{"name": str, "locations": [(float, float)]}
See the methods in this module more more details on modelling JSON values.
"""
import jpy
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Dict, List, Union, Tuple, Optional, Literal, Any
from deephaven import dtypes
from deephaven._wrapper import JObjectWrapper
from deephaven.time import InstantLike, to_j_instant
from deephaven._jpy import strict_cast
__all__ = [
"string_val",
"bool_val",
"char_val",
"byte_val",
"short_val",
"int_val",
"long_val",
"float_val",
"double_val",
"instant_val",
"big_integer_val",
"big_decimal_val",
"array_val",
"object_val",
"typed_object_val",
"object_entries_val",
"tuple_val",
"any_val",
"skip_val",
"json_val",
"JsonValue",
"JsonValueType",
"RepeatedFieldBehavior",
"ObjectField",
]
_JValue = jpy.get_type("io.deephaven.json.Value")
_JObjectValue = jpy.get_type("io.deephaven.json.ObjectValue")
_JTypedObjectValue = jpy.get_type("io.deephaven.json.TypedObjectValue")
_JArrayValue = jpy.get_type("io.deephaven.json.ArrayValue")
_JObjectEntriesValue = jpy.get_type("io.deephaven.json.ObjectEntriesValue")
_JTupleValue = jpy.get_type("io.deephaven.json.TupleValue")
_JObjectField = jpy.get_type("io.deephaven.json.ObjectField")
_JRepeatedFieldBehavior = jpy.get_type("io.deephaven.json.ObjectField$RepeatedBehavior")
_JJsonValueTypes = jpy.get_type("io.deephaven.json.JsonValueTypes")
_JBoolValue = jpy.get_type("io.deephaven.json.BoolValue")
_JCharValue = jpy.get_type("io.deephaven.json.CharValue")
_JByteValue = jpy.get_type("io.deephaven.json.ByteValue")
_JShortValue = jpy.get_type("io.deephaven.json.ShortValue")
_JIntValue = jpy.get_type("io.deephaven.json.IntValue")
_JLongValue = jpy.get_type("io.deephaven.json.LongValue")
_JFloatValue = jpy.get_type("io.deephaven.json.FloatValue")
_JDoubleValue = jpy.get_type("io.deephaven.json.DoubleValue")
_JStringValue = jpy.get_type("io.deephaven.json.StringValue")
_JSkipValue = jpy.get_type("io.deephaven.json.SkipValue")
_JInstantValue = jpy.get_type("io.deephaven.json.InstantValue")
_JInstantNumberValue = jpy.get_type("io.deephaven.json.InstantNumberValue")
_JInstantNumberValueFormat = jpy.get_type("io.deephaven.json.InstantNumberValue$Format")
_JBigIntegerValue = jpy.get_type("io.deephaven.json.BigIntegerValue")
_JBigDecimalValue = jpy.get_type("io.deephaven.json.BigDecimalValue")
_JAnyValue = jpy.get_type("io.deephaven.json.AnyValue")
_VALUE_STRING = _JJsonValueTypes.STRING
_VALUE_NULL = _JJsonValueTypes.NULL
_VALUE_INT = _JJsonValueTypes.INT
_VALUE_DECIMAL = _JJsonValueTypes.DECIMAL
_VALUE_BOOL = _JJsonValueTypes.BOOL
_VALUE_OBJECT = _JJsonValueTypes.OBJECT
_VALUE_ARRAY = _JJsonValueTypes.ARRAY
_EPOCH_SECONDS = _JInstantNumberValueFormat.EPOCH_SECONDS
_EPOCH_MILLIS = _JInstantNumberValueFormat.EPOCH_MILLIS
_EPOCH_MICROS = _JInstantNumberValueFormat.EPOCH_MICROS
_EPOCH_NANOS = _JInstantNumberValueFormat.EPOCH_NANOS
[docs]class JsonValue(JObjectWrapper):
"""The JSON Value type."""
j_object_type = _JValue
def __init__(self, j_value: jpy.JType):
self.j_value = j_value
@property
def j_object(self) -> jpy.JType:
return self.j_value
JsonValueType = Union[
JsonValue,
dtypes.DType,
type,
Dict[str, Union["JsonValueType", "ObjectField"]],
List["JsonValueType"],
Tuple["JsonValueType", ...],
]
"""The JSON value alias"""
[docs]def json_val(json_value_type: JsonValueType) -> JsonValue:
"""Creates a JsonValue from a JsonValueType.
- JsonValue is returned unchanged
- bool returns bool_val()
- int returns long_val()
- float returns double_val()
- str returns string_val()
- datetime.datetime returns instant_val()
- object returns any_val()
- Dictionaries returns object_val(json_value_type)
- Lists of length 1 returns array_val(json_value_type[0]) (Lists of other sizes are not supported)
- Tuples returns tuple_val(json_value_type)
Args:
json_value_type (JsonValueType): the JSON value type
Returns:
the JSON value
"""
if isinstance(json_value_type, JsonValue):
return json_value_type
if isinstance(json_value_type, dtypes.DType):
return _dtype_dict[json_value_type]
if isinstance(json_value_type, type):
return _type_dict[json_value_type]
if isinstance(json_value_type, Dict):
return object_val(json_value_type)
if isinstance(json_value_type, List):
if len(json_value_type) is not 1:
raise TypeError("Expected List as json type to have exactly one element")
return array_val(json_value_type[0])
if isinstance(json_value_type, Tuple):
return tuple_val(json_value_type)
raise TypeError(f"Unsupported JSON value type {type(json_value_type)}")
[docs]class RepeatedFieldBehavior(Enum):
"""
The behavior to use when a repeated field is encountered in a JSON object. For example,
.. code-block:: json
{
"foo": 42,
"foo": 43
}
"""
USE_FIRST = _JRepeatedFieldBehavior.USE_FIRST
"""Use the first field"""
ERROR = _JRepeatedFieldBehavior.ERROR
"""Raise an error"""
[docs]@dataclass
class ObjectField:
"""The object field options.
In contexts where the user needs to create an object field value and isn't changing any default values, the user can
simplify by just using the JsonValueType. For example,
.. code-block:: python
{
"name": ObjectField(str),
"age": ObjectField(int),
}
could be simplified to
.. code-block:: python
{
"name": str,
"age": int,
}
"""
value_type: JsonValueType
"""The json value type"""
aliases: Union[str, List[str]] = field(default_factory=list)
"""The field name aliases. By default, is an empty list."""
repeated_behavior: RepeatedFieldBehavior = RepeatedFieldBehavior.ERROR
"""The repeated field behavior. By default, is RepeatedFieldBehavior.ERROR."""
case_sensitive: bool = True
"""If the field name and aliases should be compared using case-sensitive equality. By default, is True."""
def _j_field_options(self, name: str) -> jpy.JType:
builder = (
_JObjectField.builder()
.name(name)
.options(json_val(self.value_type).j_value)
.repeatedBehavior(self.repeated_behavior.value)
.caseSensitive(self.case_sensitive)
)
if self.aliases:
builder.addAliases(
[self.aliases] if isinstance(self.aliases, str) else self.aliases
)
return builder.build()
def _build(
builder,
allow_missing: bool,
allow_null: bool,
allow_int: bool = False,
allow_decimal: bool = False,
allow_string: bool = False,
allow_bool: bool = False,
allow_object: bool = False,
allow_array: bool = False,
):
builder.allowMissing(allow_missing)
builder.allowedTypes(
([_VALUE_STRING] if allow_string else [])
+ ([_VALUE_NULL] if allow_null else [])
+ ([_VALUE_INT] if allow_int else [])
+ ([_VALUE_DECIMAL] if allow_decimal else [])
+ ([_VALUE_BOOL] if allow_bool else [])
+ ([_VALUE_OBJECT] if allow_object else [])
+ ([_VALUE_ARRAY] if allow_array else [])
)
[docs]def object_val(
fields: Dict[str, Union[JsonValueType, ObjectField]],
allow_unknown_fields: bool = True,
allow_missing: bool = True,
allow_null: bool = True,
repeated_field_behavior: RepeatedFieldBehavior = RepeatedFieldBehavior.ERROR,
case_sensitive: bool = True,
) -> JsonValue:
"""Creates an object value. For example, the JSON object
.. code-block:: json
{ "name": "foo", "age": 42 }
might be modelled as the object type
.. code-block:: python
object_val({ "name": str, "age": int })
In contexts where the user needs to create a JsonValueType and isn't changing any default values, the user can
simplify by using a Dict[str, Union[JsonValueType, ObjectField]]. For example,
.. code-block:: python
some_method(object_val({ "name": str, "age": int }))
could be simplified to
.. code-block:: python
some_method({ "name": str, "age": int })
Args:
fields (Dict[str, Union[JsonValueType, ObjectField]]): the fields
allow_unknown_fields (bool): if unknown fields are allow, by default is True
allow_missing (bool): if the object is allowed to be missing, by default is True
allow_null (bool): if the object is allowed to be a JSON null type, by default is True
repeated_field_behavior (RepeatedFieldBehavior): the default repeated field behavior, only used for fields that
are specified using JsonValueType, by default is RepeatedFieldBehavior.ERROR
case_sensitive (bool): if the field name and aliases should be compared using case-sensitive equality, only used
for fields that are specified using JsonValueType, by default is True
Returns:
the object value
"""
builder = _JObjectValue.builder()
_build(builder, allow_missing, allow_null, allow_object=True)
builder.allowUnknownFields(allow_unknown_fields)
for field_name, field_opts in fields.items():
field_opts = (
field_opts
if isinstance(field_opts, ObjectField)
else ObjectField(
field_opts,
repeated_behavior=repeated_field_behavior,
case_sensitive=case_sensitive,
)
)
# noinspection PyProtectedMember
builder.addFields(field_opts._j_field_options(field_name))
return JsonValue(builder.build())
[docs]def typed_object_val(
type_field: str,
shared_fields: Dict[str, Union[JsonValueType, ObjectField]],
objects: Dict[str, JsonValueType],
allow_unknown_types: bool = True,
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[str] = None,
on_null: Optional[str] = None,
) -> JsonValue:
"""Creates a type-discriminated object value. For example, the JSON objects
.. code-block:: json
{ "type": "trade", "symbol": "FOO", "price": 70.03, "size": 42 }
.. code-block:: json
{ "type": "quote", "symbol": "BAR", "bid": 10.01, "ask": 10.05 }
might be modelled as a type-discriminated object with "type" as the type field, "symbol" as a shared field, with a
"trade" object containing a "bid" and an "ask" field, and with a "quote" object containing a "price" and a "size"
field:
.. code-block:: python
typed_object_val(
"type",
{"symbol": str},
{
"quote": {
"price": float,
"size": int
},
"trade": {
"bid": float,
"ask": float
}
}
)
Args:
type_field (str): the type-discriminating field
shared_fields (Dict[str, Union[JsonValueType, ObjectField]]): the shared fields
objects (Dict[str, Union[JsonValueType, ObjectField]]): the individual objects, keyed by their
type-discriminated value. The values must be object options.
allow_unknown_types (bool): if unknown types are allow, by default is True
allow_missing (bool): if the object is allowed to be missing, by default is True
allow_null (bool): if the object is allowed to be a JSON null type, by default is True
on_missing (Optional[str]): the type value to use when the JSON value is missing and allow_missing is True,
default is None
on_null (Optional[str]): the type value to use when the JSON value is null and allow_null is True, default is
None
Returns:
the typed object value
"""
builder = _JTypedObjectValue.builder()
_build(builder, allow_missing, allow_null, allow_object=True)
builder.typeFieldName(type_field)
builder.allowUnknownTypes(allow_unknown_types)
if on_missing:
builder.onMissing(on_missing)
if on_null:
builder.onNull(on_null)
for shared_field_name, shared_field_opts in shared_fields.items():
shared_field_opts = (
shared_field_opts
if isinstance(shared_field_opts, ObjectField)
else ObjectField(
shared_field_opts,
repeated_behavior=RepeatedFieldBehavior.ERROR,
case_sensitive=True,
)
)
# noinspection PyProtectedMember
builder.addSharedFields(shared_field_opts._j_field_options(shared_field_name))
for object_name, object_type in objects.items():
builder.putObjects(
object_name, strict_cast(json_val(object_type).j_value, _JObjectValue)
)
return JsonValue(builder.build())
[docs]def array_val(
element: JsonValueType,
allow_missing: bool = True,
allow_null: bool = True,
) -> JsonValue:
"""Creates a "typed array", where all elements of the array have the same element type. For example, the JSON array
.. code-block:: json
[1, 42, 43, 13]
might be modelled as an array of ints
.. code-block:: python
array_val(int)
In contexts where the user needs to create a JsonValueType and isn't changing any default values, the user can
simplify by using a list with a single element type. For example,
.. code-block:: python
some_method(array_val(element))
could be simplified to
.. code-block:: python
some_method([element])
Args:
element (JsonValueType): the array element type
allow_missing (bool): if the array is allowed to be missing, by default is True
allow_null (bool): if the array is allowed to be a JSON null type, by default is True
Returns:
the array value
"""
builder = _JArrayValue.builder()
builder.element(json_val(element).j_value)
_build(builder, allow_missing, allow_null, allow_array=True)
return JsonValue(builder.build())
[docs]def object_entries_val(
value_type: JsonValueType,
key_type: JsonValueType = str,
allow_missing: bool = True,
allow_null: bool = True,
) -> JsonValue:
"""Creates an object entries value. This is used in situations where the number of fields in an object is
variable and all the values types are the same. For example, the JSON object
.. code-block:: json
{
"foo": 1,
"bar": 42,
"baz": 3,
...
"xyz": 100
}
might be modelled as the object kv type
.. code-block:: python
object_entries_val(int)
Args:
value_type (JsonValueType): the value type element, required
key_type (JsonValueType): the key type element, by default is type str
allow_missing (bool): if the object is allowed to be missing, by default is True
allow_null (bool): if the object is allowed to be a JSON null type, by default is True
Returns:
the object entries value
"""
builder = _JObjectEntriesValue.builder()
builder.key(json_val(key_type).j_value)
builder.value(json_val(value_type).j_value)
_build(builder, allow_missing, allow_null, allow_object=True)
return JsonValue(builder.build())
[docs]def tuple_val(
values: Union[Tuple[JsonValueType, ...], Dict[str, JsonValueType]],
allow_missing: bool = True,
allow_null: bool = True,
) -> JsonValue:
"""Creates a tuple value. For example, the JSON array
.. code-block:: json
["foo", 42, 5.72]
might be modelled as the tuple type
.. code-block:: python
tuple_val((str, int, float))
To provide meaningful names, a dictionary can be used:
.. code-block:: python
tuple_val({"name": str, "age": int, "height": float})
otherwise, default names based on the indexes of the values will be used.
In contexts where the user needs to create a JsonValueType and isn't changing any default values nor is setting
names, the user can simplify passing through a python tuple type. For example,
.. code-block:: python
some_method(tuple_val((tuple_type_1, tuple_type_2)))
could be simplified to
.. code-block:: python
some_method((tuple_type_1, tuple_type_2))
Args:
values (Union[Tuple[JsonValueType, ...], Dict[str, JsonValueType]]): the tuple value types
allow_missing (bool): if the array is allowed to be missing, by default is True
allow_null (bool): if the array is allowed to be a JSON null type, by default is True
Returns:
the tuple value
"""
if isinstance(values, Tuple):
kvs = enumerate(values)
elif isinstance(values, Dict):
kvs = values.items()
else:
raise TypeError(f"Invalid tuple type: {type(values)}")
builder = _JTupleValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_array=True,
)
for name, json_value_type in kvs:
builder.putNamedValues(str(name), json_val(json_value_type).j_value)
return JsonValue(builder.build())
[docs]def bool_val(
allow_string: bool = False,
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[bool] = None,
on_null: Optional[bool] = None,
) -> JsonValue:
"""Creates a bool value. For example, the JSON boolean
.. code-block:: json
True
might be modelled as the bool type
.. code-block:: python
bool_val()
In contexts where the user needs to create a JsonValueType and isn't changing any default values, the user can
simplify by using the python built-in bool type. For example,
.. code-block:: python
some_method(bool_val())
could be simplified to
.. code-block:: python
some_method(bool)
Args:
allow_string (bool): if the bool value is allowed to be a JSON string type, default is False
allow_missing (bool): if the bool value is allowed to be missing, default is True
allow_null (bool): if the bool value is allowed to be a JSON null type, default is True
on_missing (Optional[bool]): the value to use when the JSON value is missing and allow_missing is True, default is None
on_null (Optional[bool]): the value to use when the JSON value is null and allow_null is True, default is None
Returns:
the bool value
"""
builder = _JBoolValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_bool=True,
allow_string=allow_string,
)
if on_null:
builder.onNull(on_null)
if on_missing:
builder.onMissing(on_missing)
return JsonValue(builder.build())
[docs]def char_val(
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[str] = None,
on_null: Optional[str] = None,
) -> JsonValue:
"""Creates a char value. For example, the JSON string
.. code-block:: json
"F"
might be modelled as the char type
.. code-block:: python
char_val()
Args:
allow_missing (bool): if the char value is allowed to be missing, default is True
allow_null (bool): if the char value is allowed to be a JSON null type, default is True
on_missing (Optional[str]): the value to use when the JSON value is missing and allow_missing is True, default is None. If specified, must be a single character.
on_null (Optional[str]): the value to use when the JSON value is null and allow_null is True, default is None. If specified, must be a single character.
Returns:
the char value
"""
builder = _JCharValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_string=True,
)
if on_null:
builder.onNull(ord(on_null))
if on_missing:
builder.onMissing(ord(on_missing))
return JsonValue(builder.build())
[docs]def byte_val(
allow_decimal: bool = False,
allow_string: bool = False,
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[int] = None,
on_null: Optional[int] = None,
) -> JsonValue:
"""Creates a byte (signed 8-bit) value. For example, the JSON integer
.. code-block:: json
42
might be modelled as the byte type
.. code-block:: python
byte_val()
Args:
allow_decimal (bool): if the byte value is allowed to be a JSON decimal type, default is False
allow_string (bool): if the byte value is allowed to be a JSON string type, default is False
allow_missing (bool): if the byte value is allowed to be missing, default is True
allow_null (bool): if the byte value is allowed to be a JSON null type, default is True
on_missing (Optional[int]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[int]): the value to use when the JSON value is null and allow_null is True, default is None.
Returns:
the byte value
"""
builder = _JByteValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_int=True,
allow_decimal=allow_decimal,
allow_string=allow_string,
)
if on_null:
builder.onNull(on_null)
if on_missing:
builder.onMissing(on_missing)
return JsonValue(builder.build())
[docs]def short_val(
allow_decimal: bool = False,
allow_string: bool = False,
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[int] = None,
on_null: Optional[int] = None,
) -> JsonValue:
"""Creates a short (signed 16-bit) value. For example, the JSON integer
.. code-block:: json
30000
might be modelled as the short type
.. code-block:: python
short_val()
Args:
allow_decimal (bool): if the short value is allowed to be a JSON decimal type, default is False
allow_string (bool): if the short value is allowed to be a JSON string type, default is False
allow_missing (bool): if the short value is allowed to be missing, default is True
allow_null (bool): if the short value is allowed to be a JSON null type, default is True
on_missing (Optional[int]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[int]): the value to use when the JSON value is null and allow_null is True, default is None.
Returns:
the short value
"""
builder = _JShortValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_int=True,
allow_decimal=allow_decimal,
allow_string=allow_string,
)
if on_null:
builder.onNull(on_null)
if on_missing:
builder.onMissing(on_missing)
return JsonValue(builder.build())
[docs]def int_val(
allow_decimal: bool = False,
allow_string: bool = False,
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[int] = None,
on_null: Optional[int] = None,
) -> JsonValue:
"""Creates an int (signed 32-bit) value. For example, the JSON integer
.. code-block:: json
100000
might be modelled as the int type
.. code-block:: python
int_val()
Args:
allow_decimal (bool): if the int value is allowed to be a JSON decimal type, default is False
allow_string (bool): if the int value is allowed to be a JSON string type, default is False
allow_missing (bool): if the int value is allowed to be missing, default is True
allow_null (bool): if the int value is allowed to be a JSON null type, default is True
on_missing (Optional[int]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[int]): the value to use when the JSON value is null and allow_null is True, default is None.
Returns:
the int value
"""
builder = _JIntValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_int=True,
allow_decimal=allow_decimal,
allow_string=allow_string,
)
if on_null:
builder.onNull(on_null)
if on_missing:
builder.onMissing(on_missing)
return JsonValue(builder.build())
[docs]def long_val(
allow_decimal: bool = False,
allow_string: bool = False,
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[int] = None,
on_null: Optional[int] = None,
) -> JsonValue:
"""Creates a long (signed 64-bit) value. For example, the JSON integer
.. code-block:: json
8000000000
might be modelled as the long type
.. code-block:: python
long_val()
In contexts where the user needs to create a JsonValueType and isn't changing any default values, the user can
simplify by using the python built-in long type. For example,
.. code-block:: python
some_method(long_val())
could be simplified to
.. code-block:: python
some_method(int)
Args:
allow_decimal (bool): if the long value is allowed to be a JSON decimal type, default is False
allow_string (bool): if the long value is allowed to be a JSON string type, default is False
allow_missing (bool): if the long value is allowed to be missing, default is True
allow_null (bool): if the long value is allowed to be a JSON null type, default is True
on_missing (Optional[int]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[int]): the value to use when the JSON value is null and allow_null is True, default is None.
Returns:
the long value
"""
builder = _JLongValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_int=True,
allow_decimal=allow_decimal,
allow_string=allow_string,
)
if on_null:
builder.onNull(on_null)
if on_missing:
builder.onMissing(on_missing)
return JsonValue(builder.build())
[docs]def float_val(
allow_string: bool = False,
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[float] = None,
on_null: Optional[float] = None,
) -> JsonValue:
"""Creates a float (signed 32-bit) value. For example, the JSON decimal
.. code-block:: json
42.42
might be modelled as the float type
.. code-block:: python
float_val()
Args:
allow_string (bool): if the float value is allowed to be a JSON string type, default is False
allow_missing (bool): if the float value is allowed to be missing, default is True
allow_null (bool): if the float value is allowed to be a JSON null type, default is True
on_missing (Optional[float]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[float]): the value to use when the JSON value is null and allow_null is True, default is None.
Returns:
the float value
"""
builder = _JFloatValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_decimal=True,
allow_int=True,
allow_string=allow_string,
)
if on_null:
builder.onNull(on_null)
if on_missing:
builder.onMissing(on_missing)
return JsonValue(builder.build())
[docs]def double_val(
allow_string: bool = False,
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[float] = None,
on_null: Optional[float] = None,
) -> JsonValue:
"""Creates a double (signed 64-bit) value. For example, the JSON decimal
.. code-block:: json
42.42424242
might be modelled as the double type
.. code-block:: python
double_val()
In contexts where the user needs to create a JsonValueType and isn't changing any default values, the user can
simplify by using the python built-in float type. For example,
.. code-block:: python
some_method(double_val())
could be simplified to
.. code-block:: python
some_method(float)
Args:
allow_string (bool): if the double value is allowed to be a JSON string type, default is False
allow_missing (bool): if the double value is allowed to be missing, default is True
allow_null (bool): if the double value is allowed to be a JSON null type, default is True
on_missing (Optional[int]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[int]): the value to use when the JSON value is null and allow_null is True, default is None.
Returns:
the double value
"""
builder = _JDoubleValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_decimal=True,
allow_int=True,
allow_string=allow_string,
)
if on_null:
builder.onNull(on_null)
if on_missing:
builder.onMissing(on_missing)
return JsonValue(builder.build())
[docs]def string_val(
allow_int: bool = False,
allow_decimal: bool = False,
allow_bool: bool = False,
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[str] = None,
on_null: Optional[str] = None,
) -> JsonValue:
"""Creates a String value. For example, the JSON string
.. code-block:: json
"Hello, world!"
might be modelled as the string type
.. code-block:: python
string_val()
In contexts where the user needs to create a JsonValueType and isn't changing any default values, the user can
simplify by using the python built-in str type. For example,
.. code-block:: python
some_method(string_val())
could be simplified to
.. code-block:: python
some_method(str)
Args:
allow_int (bool): if the string value is allowed to be a JSON integer type, default is False
allow_decimal (bool): if the string value is allowed to be a JSON decimal type, default is False
allow_bool (bool): if the string value is allowed to be a JSON boolean type, default is False
allow_missing (bool): if the double value is allowed to be missing, default is True
allow_null (bool): if the double value is allowed to be a JSON null type, default is True
on_missing (Optional[str]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[str]): the value to use when the JSON value is null and allow_null is True, default is None.
Returns:
the String value
"""
builder = _JStringValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_string=True,
allow_int=allow_int,
allow_decimal=allow_decimal,
allow_bool=allow_bool,
)
if on_null:
builder.onNull(on_null)
if on_missing:
builder.onMissing(on_missing)
return JsonValue(builder.build())
[docs]def instant_val(
allow_missing: bool = True,
allow_null: bool = True,
number_format: Literal[None, "s", "ms", "us", "ns"] = None,
allow_decimal: bool = False,
on_missing: Optional[InstantLike] = None,
on_null: Optional[InstantLike] = None,
) -> JsonValue:
"""Creates an Instant value. For example, the JSON string
.. code-block:: json
"2009-02-13T23:31:30.123456789Z"
might be modelled as the Instant type
.. code-block:: python
instant_val()
In another example, the JSON decimal
.. code-block:: json
1234567890.123456789
might be modelled as the Instant type
.. code-block:: python
instant_val(number_format="s", allow_decimal=True)
In contexts where the user needs to create a JsonValueType and isn't changing any default values, the user can
simplify by using the python datetime.datetime type. For example,
.. code-block:: python
some_method(instant_val())
could be simplified to
.. code-block:: python
some_method(datetime.datetime)
Args:
allow_missing (bool): if the Instant value is allowed to be missing, default is True
allow_null (bool): if the Instant value is allowed to be a JSON null type, default is True
number_format (Literal[None, "s", "ms", "us", "ns"]): when set, signifies that a JSON numeric type is expected.
"s" is for seconds, "ms" is for milliseconds, "us" is for microseconds, and "ns" is for nanoseconds since
the epoch. When not set, a JSON string in the ISO-8601 format is expected.
allow_decimal (bool): if the Instant value is allowed to be a JSON decimal type, default is False. Only valid
when number_format is specified.
on_missing (Optional[InstantLike]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[InstantLike]): the value to use when the JSON value is null and allow_null is True, default is None.
Returns:
the Instant value
"""
if number_format:
builder = _JInstantNumberValue.builder()
if on_missing:
builder.onMissing(to_j_instant(on_missing))
if on_null:
builder.onNull(to_j_instant(on_null))
_build(
builder,
allow_missing,
allow_null,
allow_int=True,
allow_decimal=allow_decimal,
)
if number_format == "s":
builder.format(_EPOCH_SECONDS)
elif number_format == "ms":
builder.format(_EPOCH_MILLIS)
elif number_format == "us":
builder.format(_EPOCH_MICROS)
elif number_format == "ns":
builder.format(_EPOCH_NANOS)
else:
raise TypeError(f"Invalid number format: {number_format}")
return JsonValue(builder.build())
else:
if allow_decimal:
raise TypeError("allow_decimal is only valid when using number_format")
builder = _JInstantValue.builder()
if on_missing:
builder.onMissing(to_j_instant(on_missing))
if on_null:
builder.onNull(to_j_instant(on_null))
_build(
builder,
allow_missing,
allow_null,
allow_string=True,
)
return JsonValue(builder.build())
[docs]def big_integer_val(
allow_string: bool = False,
allow_decimal: bool = False,
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[Union[int, str]] = None,
on_null: Optional[Union[int, str]] = None,
) -> JsonValue:
"""Creates a BigInteger value. For example, the JSON integer
.. code-block:: json
123456789012345678901
might be modelled as the BigInteger type
.. code-block:: python
big_integer_val()
Args:
allow_string (bool): if the BigInteger value is allowed to be a JSON string type, default is False.
allow_decimal (bool): if the BigInteger value is allowed to be a JSON decimal type, default is False.
allow_missing (bool): if the BigInteger value is allowed to be missing, default is True
allow_null (bool): if the BigInteger value is allowed to be a JSON null type, default is True
on_missing (Optional[Union[int, str]]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[Union[int, str]]): the value to use when the JSON value is null and allow_null is True, default is None.
Returns:
the BigInteger value
"""
builder = _JBigIntegerValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_int=True,
allow_decimal=allow_decimal,
allow_string=allow_string,
)
if on_missing:
builder.onMissing(dtypes.BigInteger(str(on_missing)))
if on_null:
builder.onNull(dtypes.BigInteger(str(on_null)))
return JsonValue(builder.build())
[docs]def big_decimal_val(
allow_string: bool = False,
allow_missing: bool = True,
allow_null: bool = True,
on_missing: Optional[Union[float, str]] = None,
on_null: Optional[Union[float, str]] = None,
) -> JsonValue:
"""Creates a BigDecimal value. For example, the JSON decimal
.. code-block:: json
123456789012345678901.42
might be modelled as the BigDecimal type
.. code-block:: python
big_decimal_val()
Args:
allow_string (bool): if the BigDecimal value is allowed to be a JSON string type, default is False.
allow_missing (bool): if the BigDecimal value is allowed to be missing, default is True
allow_null (bool): if the BigDecimal value is allowed to be a JSON null type, default is True
on_missing (Optional[Union[float, str]]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[Union[float, str]]): the value to use when the JSON value is null and allow_null is True, default is None.
Returns:
the BigDecimal value
"""
builder = _JBigDecimalValue.builder()
_build(
builder,
allow_missing,
allow_null,
allow_int=True,
allow_decimal=True,
allow_string=allow_string,
)
if on_missing:
builder.onMissing(dtypes.BigDecimal(str(on_missing)))
if on_null:
builder.onNull(dtypes.BigDecimal(str(on_null)))
return JsonValue(builder.build())
[docs]def any_val() -> JsonValue:
"""Creates an "any" value. The resulting type is implementation dependant.
Returns:
the "any" value
"""
return JsonValue(_JAnyValue.of())
[docs]def skip_val(
allow_missing: Optional[bool] = None,
allow_null: Optional[bool] = None,
allow_int: Optional[bool] = None,
allow_decimal: Optional[bool] = None,
allow_string: Optional[bool] = None,
allow_bool: Optional[bool] = None,
allow_object: Optional[bool] = None,
allow_array: Optional[bool] = None,
allow_by_default: bool = True,
) -> JsonValue:
"""Creates a "skip" value. No resulting type will be returned, but the JSON types will be validated as configured.
This may be useful in combination with an object type where allow_unknown_fields=False. For example, the JSON object
.. code-block:: json
{ "name": "foo", "age": 42 }
might be modelled as the object type
.. code-block:: python
object_val({ "name": str, "age": skip_val() }, allow_unknown_fields=False)
Args:
allow_missing (Optional[bool]): if a missing JSON value is allowed, by default is None
allow_null (Optional[bool]): if a JSON null type is allowed, by default is None
allow_int (Optional[bool]): if a JSON integer type is allowed, by default is None
allow_decimal (Optional[bool]): if a JSON decimal type is allowed, by default is None
allow_string (Optional[bool]): if a JSON string type is allowed, by default is None
allow_bool (Optional[bool]): if a JSON boolean type is allowed, by default is None
allow_object (Optional[bool]): if a JSON object type is allowed, by default is None
allow_array (Optional[bool]): if a JSON array type is allowed, by default is None
allow_by_default (bool): the default behavior for the other arguments when they are set to None, by default is True
Returns:
the "skip" value
"""
def _allow(x: Optional[bool]) -> bool:
return x if x is not None else allow_by_default
builder = _JSkipValue.builder()
_build(
builder,
allow_missing=_allow(allow_missing),
allow_null=_allow(allow_null),
allow_int=_allow(allow_int),
allow_decimal=_allow(allow_decimal),
allow_string=_allow(allow_string),
allow_bool=_allow(allow_bool),
allow_object=_allow(allow_object),
allow_array=_allow(allow_array),
)
return JsonValue(builder.build())
_dtype_dict = {
dtypes.bool_: bool_val(),
dtypes.char: char_val(),
dtypes.int8: byte_val(),
dtypes.int16: short_val(),
dtypes.int32: int_val(),
dtypes.int64: long_val(),
dtypes.float32: float_val(),
dtypes.float64: double_val(),
dtypes.string: string_val(),
dtypes.Instant: instant_val(),
dtypes.BigInteger: big_integer_val(),
dtypes.BigDecimal: big_decimal_val(),
dtypes.JObject: any_val(),
dtypes.bool_array: array_val(bool_val()),
dtypes.char_array: array_val(char_val()),
dtypes.int8_array: array_val(byte_val()),
dtypes.int16_array: array_val(short_val()),
dtypes.int32_array: array_val(int_val()),
dtypes.int64_array: array_val(long_val()),
dtypes.float32_array: array_val(float_val()),
dtypes.float64_array: array_val(double_val()),
dtypes.string_array: array_val(string_val()),
dtypes.instant_array: array_val(instant_val()),
}
_type_dict = {
bool: bool_val(),
int: long_val(),
float: double_val(),
str: string_val(),
datetime: instant_val(),
object: any_val(),
}