JSON utilities

JSON (de)serialization framework.

The framework presented here is somewhat based on Go’s “json” package (especially the omitempty functionality).

josepy.json_util.field(json_name: str, default: Optional[Any] = None, omitempty: bool = False, decoder: Optional[Callable[[Any], Any]] = None, encoder: Optional[Callable[[Any], Any]] = None) Any[source]

Convenient function to declare a Field with proper type annotations.

This function allows to write the following code:

import josepy class JSON(josepy.JSONObjectWithFields):

typ: str = josepy.field(‘type’)

def other_type(self) -> str:

return self.typ

class josepy.json_util.Field(json_name: str, default: Optional[Any] = None, omitempty: bool = False, decoder: Optional[Callable[[Any], Any]] = None, encoder: Optional[Callable[[Any], Any]] = None)[source]

JSON object field.

Field is meant to be used together with JSONObjectWithFields.

encoder (decoder) is a callable that accepts a single parameter, i.e. a value to be encoded (decoded), and returns the serialized (deserialized) value. In case of errors it should raise SerializationError (DeserializationError).

Note, that decoder should perform partial serialization only.

Variables:
  • json_name (str) – Name of the field when encoded to JSON.

  • default – Default value (used when not present in JSON object).

  • omitempty (bool) – If True and the field value is empty, then it will not be included in the serialized JSON object, and default will be used for deserialization. Otherwise, if False, field is considered as required, value will always be included in the serialized JSON objected, and it must also be present when deserializing.

omit(value: Any) bool[source]

Omit the value in output?

decoder(fdec: Callable[[Any], Any]) Field[source]

Descriptor to change the decoder on JSON object field.

encoder(fenc: Callable[[Any], Any]) Field[source]

Descriptor to change the encoder on JSON object field.

decode(value: Any) Any[source]

Decode a value, optionally with context JSON object.

encode(value: Any) Any[source]

Encode a value, optionally with context JSON object.

classmethod default_decoder(value: Any) Any[source]

Default decoder.

Recursively deserialize into immutable types ( josepy.util.frozendict instead of dict(), tuple() instead of list()).

classmethod default_encoder(value: Any) Any[source]

Default (passthrough) encoder.

class josepy.json_util.JSONObjectWithFieldsMeta(name: str, bases: List[str], namespace: Dict[str, Any])[source]

Metaclass for JSONObjectWithFields and its subclasses.

It makes sure that, for any class cls with __metaclass__ set to JSONObjectWithFieldsMeta:

  1. All fields (attributes of type Field) in the class definition are moved to the cls._fields dictionary, where keys are field attribute names and values are fields themselves.

  2. cls.__slots__ is extended by all field attribute names (i.e. not Field.json_name). Original cls.__slots__ are stored in cls._orig_slots.

In a consequence, for a field attribute name some_field, cls.some_field will be a slot descriptor and not an instance of Field. For example:

some_field = Field('someField', default=())

class Foo:
    __metaclass__ = JSONObjectWithFieldsMeta
    __slots__ = ('baz',)
    some_field = some_field

assert Foo.__slots__ == ('some_field', 'baz')
assert Foo._orig_slots == ()
assert Foo.some_field is not Field

assert Foo._fields.keys() == ['some_field']
assert Foo._fields['some_field'] is some_field

As an implementation note, this metaclass inherits from abc.ABCMeta (and not the usual type) to mitigate the metaclass conflict (ImmutableMap and JSONDeSerializable, parents of JSONObjectWithFields, use abc.ABCMeta as its metaclass).

class josepy.json_util.JSONObjectWithFields(**kwargs: Any)[source]

JSON object with fields.

Example:

class Foo(JSONObjectWithFields):
    bar = Field('Bar')
    empty = Field('Empty', omitempty=True)

    @bar.encoder
    def bar(value):
        return value + 'bar'

    @bar.decoder
    def bar(value):
        if not value.endswith('bar'):
            raise errors.DeserializationError('No bar suffix!')
        return value[:-3]

assert Foo(bar='baz').to_partial_json() == {'Bar': 'bazbar'}
assert Foo.from_json({'Bar': 'bazbar'}) == Foo(bar='baz')
assert (Foo.from_json({'Bar': 'bazbar', 'Empty': '!'})
        == Foo(bar='baz', empty='!'))
assert Foo(bar='baz').bar == 'baz'
encode(name: str) Any[source]

Encode a single field.

Parameters:

name (str) – Name of the field to be encoded.

Raises:
fields_to_partial_json() Dict[str, Any][source]

Serialize fields to JSON.

to_partial_json() Dict[str, Any][source]

Partially serialize.

Following the example, partial serialization means the following:

assert isinstance(Bar().to_partial_json()[0], Foo)
assert isinstance(Bar().to_partial_json()[1], Foo)

# in particular...
assert Bar().to_partial_json() != ['foo', 'foo']
Raises:

josepy.errors.SerializationError – in case of any serialization error.

Returns:

Partially serializable object.

classmethod fields_from_json(jobj: Mapping[str, Any]) Any[source]

Deserialize fields from JSON.

classmethod from_json(jobj: Mapping[str, Any]) GenericJSONObjectWithFields[source]

Deserialize a decoded JSON document.

Parameters:

jobj – Python object, composed of only other basic data types, as decoded from JSON document. Not necessarily dict (as decoded from “JSON object” document).

Raises:

josepy.errors.DeserializationError – if decoding was unsuccessful, e.g. in case of unparseable X509 certificate, or wrong padding in JOSE base64 encoded string, etc.

josepy.json_util.encode_b64jose(data: bytes) str[source]

Encode JOSE Base-64 field.

Parameters:

data (bytes) –

Return type:

str

josepy.json_util.decode_b64jose(data: str, size: Optional[int] = None, minimum: bool = False) bytes[source]

Decode JOSE Base-64 field.

Parameters:
  • data (unicode) –

  • size (int) – Required length (after decoding).

  • minimum (bool) – If True, then size will be treated as minimum required length, as opposed to exact equality.

Return type:

bytes

josepy.json_util.encode_hex16(value: bytes) str[source]

Hexlify.

Parameters:

value (bytes) –

Return type:

unicode

josepy.json_util.decode_hex16(value: str, size: Optional[int] = None, minimum: bool = False) bytes[source]

Decode hexlified field.

Parameters:
  • value (unicode) –

  • size (int) – Required length (after decoding).

  • minimum (bool) – If True, then size will be treated as minimum required length, as opposed to exact equality.

Return type:

bytes

josepy.json_util.encode_cert(cert: ComparableX509) str[source]

Encode certificate as JOSE Base-64 DER.

Return type:

unicode

josepy.json_util.decode_cert(b64der: str) ComparableX509[source]

Decode JOSE Base-64 DER-encoded certificate.

Parameters:

b64der (unicode) –

Return type:

OpenSSL.crypto.X509 wrapped in ComparableX509

josepy.json_util.encode_csr(csr: ComparableX509) str[source]

Encode CSR as JOSE Base-64 DER.

Return type:

unicode

josepy.json_util.decode_csr(b64der: str) ComparableX509[source]

Decode JOSE Base-64 DER-encoded CSR.

Parameters:

b64der (unicode) –

Return type:

OpenSSL.crypto.X509Req wrapped in ComparableX509

class josepy.json_util.TypedJSONObjectWithFields(**kwargs: Any)[source]

JSON object with type.

typ: str = NotImplemented

Type of the object. Subclasses must override.

type_field_name: str = 'type'

Field name used to distinguish different object types.

Subclasses will probably have to override this.

TYPES: Dict[str, Type] = NotImplemented

Types registered for JSON deserialization

classmethod register(type_cls: Type[GenericTypedJSONObjectWithFields], typ: Optional[str] = None) Type[GenericTypedJSONObjectWithFields][source]

Register class for JSON deserialization.

classmethod get_type_cls(jobj: Mapping[str, Any]) Type[TypedJSONObjectWithFields][source]

Get the registered class for jobj.

to_partial_json() Dict[str, Any][source]

Get JSON serializable object.

Returns:

Serializable JSON object representing ACME typed object. validate() will almost certainly not work, due to reasons explained in josepy.interfaces.IJSONSerializable.

Return type:

dict

classmethod from_json(jobj: Mapping[str, Any]) TypedJSONObjectWithFields[source]

Deserialize ACME object from valid JSON object.

Raises:

josepy.errors.UnrecognizedTypeError – if type of the ACME object has not been registered.