# Primitive Types ## Built-in primitive types These are the built-in primitive types accepted by jsonbp and the corresponding Python types that they map to/from: | jsonbp | Python | | ------ | ------ | | Integer | int | | Float | float | | Decimal | decimal.Decimal | | Bool | bool | | instant | datetime.datetime | | String | str | When used in declarations, primitive types can be customized through **specificities**, which are a list of key-value pairs enclosed in parenthesis and separated by commas. Each type has a fixed and well defined list of possible specificities. For example: ``` object WeekInstant { hours: Integer (min=0, max=12), minutes : Integer (min=0, max=59), seconds : Integer (min=0, max=59), timezone: String (minLength=3, maxLength=3), DST: Bool (coerce=false) } ``` The following is a list of all possible specificities by primitive type: | type | specificity | Default | | ------ | ------ | ------ | | Integer | min
max | -2,147,483,648
+2,147,483,647 | | Float | format
atLeast
atMost
greaterThan
lessThan
allowNaN
| %g
-infinity
+infinity
NaN
NaN
false | | Decimal | precision
min
max
radix
separator
indianFormat
prefix
suffix | 2
-2,147,483,648.00
+2,147,483,648.00
. (dot)
(empty string)
false
(empty string)
(empty string) | | Bool | coerce | false | | Instant | iso
isoResolution
format | true
milliseconds
%Y-%m-%dT%H:%M:%S%z | | String | minLength
maxLength
format | 0
1024
.* | Some of the specificities may warrant an explanation: **Float** *atLeast* and *atMost*: Defines closed intervals *greaterThan* and *lessThan*: Defines open intervals *format*: Format for serializing floats in a desired way. [More Info](https://docs.python.org/3/library/string.html#format-specification-mini-language) **Decimal** *precision*: Number of digits after the radix *radix*: Character that represents the radix *separator*: Character used to organize and simplify reading large numbers *indianFormat*: Whether to use the indian numbering system *prefix*: Leading text to strip during deserialization or add during serialization *suffix*: Trailing text to strip during deserialization or add during serialization **Bool** *coerce*: If set to false, only **true** and **false** are acceptable boolean values. Otherwise (if "coerce" is true) during deserialization, truthy values will be accepted as **true** and falsy values will be accepted as **false**. **Instant** *iso*: Whether to use ISO 8601 format or not *isoResolution*: When *iso* is true, defines which resolution to use. Possible values can be found [here](https://docs.python.org/3/library/datetime.html#datetime.datetime.isoformat) *format*: Defines which format to use when *iso* is false. The format will be directy passed to [strftime() and strptime()](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior) **String** *format*: Regular expression defining the pattern a string must conform to in order to be accepted as valid input. ## Custom primitive types It's possible to load and use custom primitive types or even overwrite the builtin ones offered by jsonbp. This can be achieved writing a Python script with a dictionary named **type_specs** in which the following fields need to be defined: **name**: name of the primitive type **parser**: function that receives a string and the specificities, validates its contents, and returns deserialized Python data **formatter**: function that receives a Python data and the specificities, validates its contents, and returns a string **defaults**: dictionary with the specificities allowed for the type and its default values *parser* and *formatter* functions should return a tuple in the form *(success, outcome)* where **success** indicates whether the operation succeed. If **success** is true, outcome needs to be the resulting value. If **success** is false, outcome should be a dictionary with the following contents: **error**: type of error that was caught **context**: dicionary holding the context with the values which led to the error The possible errors types are exported in **jsonbp.ErrorType**. They are listed below: | ErrorType | Context | | ------ | ------ | | JSON_PARSING | line, column, message | | VALUE_PARSING | type | | NULL_VALUE | field | | OUTSIDE_RANGE | value | | INVALID_LENGTH | length | | UNKNOWN_LITERAL | | | INVALID_ENUM | | | INVALID_FORMAT | | | MISSING_FIELD | field | | INVALID_ARRAY | | | INVALID_OBJECT | | If an exception is raised and propagates from either *parser()* or *formatter()*, it'll be caught and a VALUE_PARSING error will be flagged with **type** in the context indicating the name of the primitive type. For example, the following code defines a primitive type that only accepts even integers: ```py import jsonbp def _format(value, specs): return str(value) def _parse(value, specs): intValue = int(value) if intValue % 2 != 0: return False, { "error": jsonbp.ErrorType.OUTSIDE_RANGE, "context": {"value": intValue} } return True, intValue type_specs = { 'name': 'Even', 'parser': _parse, 'formatter': _format, 'defaults': dict() } ``` All jsonbp builtin types are defined in this way, and their source can be consulted for further reference. To actually make these custom types available, the functions **jsonbp.load_string()** and **jsonbp.load_file()** accept the named argument **custom_types**, which should be a list of directories to scan for extra primitive type definitions. If a custom primitive type has the same name as one of the builtin primitive types, it'll override its original definition and display a warning in the terminal, which you can safely ignore if that is your intention.