• Stars
    star
    233
  • Rank 171,256 (Top 4 %)
  • Language
    Python
  • License
    Other
  • Created about 11 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A simple json-object mapping for Python

jsonobject

Build Status Downloads Supported Versions Contributors

jsonobject is a python library for handling deeply nested JSON objects as well-schema'd python objects.

jsonobject is made by Dimagi, where we build, use, and contribute to OSS in our mission to reduce inequity in the world.

jsonobject is inspired by and largely API compatible with the Document/DocumentSchema portion of couchdbkit. Because jsonobject is not only simpler and standalone, but also faster, we also maintain a fork of couchdbkit, jsonobject-couchdbkit, that is backed by jsonobject and works seamlessly as a swap-in replacement for the main library.

It is used heavily in CommCare HQ (source), and the API is largely stable, but more advanced features may change in the future.

Getting Started

To install using pip, simply run

pip install jsonobject

Example

The code below defines a simple user model, and its natural mapping to JSON.

from jsonobject import *

class User(JsonObject):
    username = StringProperty()
    name = StringProperty()
    active = BooleanProperty(default=False)
    date_joined = DateTimeProperty()
    tags = ListProperty(unicode)

Once it is defined, it can be used to wrap or produce deserialized JSON.

>>> user1 = User(
    name='John Doe',
    username='jdoe',
    date_joined=datetime.datetime.utcnow(),
    tags=['generic', 'anonymous']
)
>>> user1.to_json()
{
    'name': u'John Doe',
    'username': u'jdoe',
    'active': False,
    'date_joined': '2013-08-05T02:46:58Z',
    'tags': [u'generic', u'anonymous']
}

Notice that the datetime is converted to an ISO format string in JSON, but is a real datetime on the object:

>>> user1.date_joined
datetime.datetime(2013, 8, 5, 2, 46, 58)

The jsonobject Constructor

A JsonObject subclass that has been defined as User above comes with a lot of built-in functionality. The basic operations are

  1. Make a new object from deserialized JSON (e.g. the output of json.loads)
  2. Construct a new object with given values
  3. Modify an object
  4. Dump to deserialized json (e.g. the input of json.dumps)

1 & 2 are accomplished with the constructor. There are two main ways to call the constructor:

User(
    name='John Doe',
    username='jdoe',
    date_joined=datetime.datetime.utcnow(),
    tags=['generic', 'anonymous']
)

as above (satisfies #2) and

User({
    'name': u'John Doe',
    'username': u'jdoe',
    'active': False,
    'date_joined': '2013-08-05T02:46:58Z',
    'tags': [u'generic', u'anonymous']
})

(satisfies #1). These two styles can also be mixed and matched:

User({
    'name': u'John Doe',
    'username': u'jdoe',
    'active': False,
    'tags': [u'generic', u'anonymous']
}, date_joined=datetime.datetime.utcnow())

Notice how datetimes are stored as strings in the deserialized JSON, but as datetime.datetimes in the nice python object—we will refer to these as the "json" representation and the "python" representation, or alternatively the "unwrapped" representation and the "wrapped" representation.

Gotcha. When calling the constructor, remember that the keyword argument style requires you to pass in the "python" representation (e.g. a datetime) while the json-wrapping style of passing in a dict requires you to give it in the "json" representation (e.g. a datetime-formatted string).

Property Types

There are two main kinds of property types: scalar types (like string, bool, int, datetime, etc.) and container types (list, dict, set). They are dealt with separately below.

Scalar Types

All scalar properties can take the value None in addition to the values particular to their type (strings, bools, etc). If set to the wrong type, properties raise a jsonobject.exceptions.BadValueError:

class Foo(jsonobject.JsonObject):
    b = jsonobject.BooleanProperty()
>>> Foo(b=0)
Traceback (most recent call last):
  [...]
jsonobject.exceptions.BadValueError: 0 not of type <type 'bool'>

jsonobject.StringProperty

Maps to a unicode. Usage:

class Foo(jsonobject.JsonObject):
    s = jsonobject.StringProperty()

If you set it to an ascii str it will implicitly convert to unicode:

>>> Foo(s='hi')  # converts to unicode
Foo(s=u'hi')

If you set it to a non-ascii str, it will fail with a UnicodeDecodeError:

>>> Foo(s='\xff')
Traceback (most recent call last):
  [...]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0: ordinal not in range(128)

jsonobject.BooleanProperty

Maps to a bool.

jsonobject.IntegerProperty

Maps to an int or long.

jsonobject.FloatProperty

Maps to a float.

jsonobject.DecimalProperty

Maps to a decimal.Decimal and stored as a JSON string. This type, unlike FloatProperty, stores the "human" representation of the digits. Usage:

class Foo(jsonobject.JsonObject):
    number = jsonobject.DecimalProperty()

If you set it to an int or float, it will implicitly convert to Decimal:

>>> Foo(number=1)
Foo(number=Decimal('1'))
>>> Foo(number=1.2)
Foo(number=Decimal('1.2'))

If you set it to a str or unicode, however, it raises an AssertionError:

>>> Foo(number='1.0')
Traceback (most recent call last):
  [...]
AssertionError

Todo: this should really raise a BadValueError.

If you pass in json in which the Decimal value is a str or unicode, but it is malformed, it throws the same errors as decimal.Decimal.

>>> Foo({'number': '1.0'})
Foo(number=Decimal('1.0'))
>>> Foo({'number': '1.0.0'})
Traceback (most recent call last):
  [...]
decimal.InvalidOperation: Invalid literal for Decimal: '1.0.0'

jsonobject.DateProperty

Maps to a datetime.date and stored as a JSON string of the format '%Y-%m-%d'. Usage:

class Foo(jsonobject.JsonObject):
    date = jsonobject.DateProperty()

Wrapping a badly formatted string raises a BadValueError:

>>> Foo({'date': 'foo'})
Traceback (most recent call last):
  [...]
jsonobject.exceptions.BadValueError: 'foo' is not a date-formatted string

jsonobject.DateTimeProperty

Maps to a timezone-unaware datetime.datetime and stored as a JSON string of the format '%Y-%m-%dT%H:%M:%SZ'.

While it works perfectly with good inputs, it is extremely sloppy when it comes to dealing with inputs that don't match the exact specified format. Rather than matching stricty, it simply truncates the string to the first 19 characters and tries to parse that as '%Y-%m-%dT%H:%M:%S'. This ignores both microseconds and, even worse, the timezone. This is a holdover from couchdbkit.

In newer versions of jsonboject, you may optionally specify a DateTimeProperty as exact:

class Foo(jsonobject.JsonObject):
    date = jsonobject.DateTimeProperty(exact=True)

This provides a much cleaner conversion model that has the following properties:

  1. It preserves microseconds
  2. The incoming JSON representation must match '%Y-%m-%dT%H:%M:%S.%fZ' exactly. (This is similar to the default output, except for the mandatory 6 decimal places, i.e. milliseconds.)
  3. Representations that don't match exactly will be rejected with a BadValueError.

Recommendation: If you are not locked into couchdbkit's earlier bad behavior, you should always use the exact=True flag on DateTimePropertys and TimePropertys (below).

jsonobject.TimeProperty

Maps to a datetime.time, stored as a JSON string of the format '%H:%M:%S'.

To get access to milliseconds and strict behavior, use the exact=True setting which strictly accepts the format '%H:%M:%S.%f'. This is always recommended. For more information please read the previous section on DateTimeProperty.

Container Types

Container types generally take a first argument, item_type, specifying the type of the contained objects.

jsonobject.ObjectProperty(item_type)

Maps to a dict that has a schema specified by item_type, which must be itself a subclass of JsonObject. Usage:

class Bar(jsonobject.JsonObject):
    name = jsonobject.StringProperty()


class Foo(jsonobject.JsonObject):
    bar = jsonobject.ObjectProperty(Bar)

If not specified, it will be set to a new object with default values:

>>> Foo()
Foo(bar=Bar(name=None))

If you want it set to None you must do so explicitly.

jsonobject.ListProperty(item_type)

Maps to a list with items of type item_type, which can be any of the following:

  • an instance of a property class. This is the most flexible option, and all validation (required, etc.) will be done as as specified by the property instance.
  • a property class, which will be instantiated with required=True
  • one of their corresponding python types (i.e. int for IntegerProperty, etc.)
  • a JsonObject subclass

Note that a property class (as well as the related python type syntax) will be instantiated with required=True, so ListProperty(IntegerProperty) and ListProperty(int) do not allow None, and ListProperty(IntegerProperty()) does allow None.

The serialization behavior of whatever item type is given is recursively applied to each member of the list.

If not specified, it will be set to an empty list.

jsonobject.SetProperty(item_type)

Maps to a set and stored as a list (with only unique elements). Otherwise its behavior is very much like ListProperty's.

jsonobject.DictProperty(item_type)

Maps to a dict with string keys and values specified by item_type. Otherwise its behavior is very much like ListProperty's.

If not specified, it will be set to an empty dict.

Other

jsonobject.DefaultProperty()

This flexibly wraps any valid JSON, including all scalar and container types, dynamically detecting the value's type and treating it with the corresponding property.

Property options

Certain parameters may be passed in to any property.

For example, required is one such parameter in the example below:

class User(JsonObject):
    username = StringProperty(required=True)

Here is a complete list of properties:

  • default

    Specifies a default value for the property

  • name

    The name of the property within the JSON representation*. This defaults to the name of the python property, but you can override it if you wish. This can be useful, for example, to get around conflicting with python keywords:

    >>> class Route(JsonObject):
    ...     from_ = StringProperty(name='from')
    ...     to = StringProperty()  # name='to' by default
    >>> Route(from_='me', to='you').to_json()
    {'from': u'me', 'to': u'you'}

    Notice how an underscore is present in the python property name ('from_'), but absent in the JSON property name ('from').

    \*If you're wondering how `StringProperty`'s `name` parameter could possibly default to `to` in the example above, when it doesn't have access to the `Route` class's properties at init time, you're completely right. The behavior described is implemented in `JsonObject`'s `__metaclass__`, which *does* have access to the `Route` class's properties.
  • choices

    A list of allowed values for the property. (Unless otherwise specified, None is also an allowed value.)

  • required

    Defaults to False. For scalar properties requires means that the value None may not be used. For container properties it means they may not be empty or take the value None.

  • exclude_if_none

    Defaults to False. When set to true, this property will be excluded from the JSON output when its value is falsey. (Note that currently this is at odds with the parameter's name, since the condition is that it is falsey, not that it is None).

  • validators

    A single validator function or list of validator functions. Each validator function should raise an exception on invalid input and do nothing otherwise.

  • verbose_name

    This property does nothing and was added to match couchdbkit's API.

Performance Comparison with Couchdbkit

In order to do a direct comparison with couchdbkit, the test suite includes a large sample schema originally written with couchdbkit. It is easy to swap in jsonobject for couchdbkit and run the tests with each. Here are the results:

$ python -m unittest test.test_couchdbkit
....
----------------------------------------------------------------------
Ran 4 tests in 1.403s

OK
$ python -m unittest test.test_couchdbkit
....
----------------------------------------------------------------------
Ran 4 tests in 0.153s

OK

Development Lifecycle

jsonobject versions follow semantic versioning. Version information can be found in CHANGES.md.

Information for developers and maintainers, such as how to run tests and release new versions, can be found in LIFECYCLE.md.

More Repositories

1

commcare-hq

CommCareHQ is the server backend for CommCare, the world's largest platform for designing, managing, and deploying robust, offline-first, mobile applications to frontline workers worldwide
Python
468
star
2

django-cte

Common Table Expressions (CTE) for Django
Python
235
star
3

django-prbac

Python
126
star
4

Vellum

An XForms form designer written purely in Javascript
JavaScript
75
star
5

required-labels

🏷 Automated label checking for GitHub pull requests
Python
46
star
6

open-source

Dimagi's Open Source project standards and Code Review Policies
38
star
7

zebra-print-android

An Android application library which provides a one-shot intent for connecting to a Zebra printer and providing a ZPL file to be printed.
Java
34
star
8

gmail-filters

Tool for creating basic Gmail filters
Python
32
star
9

commcare-android

Offline First Android software client for CommCare, the world's largest platform for designing, managing, and deploying robust mobile applications to frontline workers worldwide
Java
32
star
10

commcare-cloud

Tools for standing up and managing a CommCare HQ server environment
Python
29
star
11

js-xpath

XPath Parser for Javascript
JavaScript
28
star
12

rapidandroid

A Java based RapidSMS port on the Android Platform
Java
20
star
13

AadharUID

Java
17
star
14

logistics

Python
15
star
15

datadog-checks

Custom Datadog checks
Python
14
star
16

couchforms

XForms processing engine in django and couchdb
Python
14
star
17

open-chat-studio

A web based platform for building Chatbots backed by Large Language Models
Python
13
star
18

data-hq

A domain management and xforms-processing application
Python
13
star
19

commcare-core

Core Java engine for CommCare, the world's largest platform for designing, managing, and deploying robust Offline First mobile applications to frontline workers worldwide
Java
12
star
20

rapidsms-logistics

an app to assist with logistics management using rapidsms
Python
10
star
21

touchforms

[deprecated] web-based javarosa xforms player built for touchscreen kiosks
JavaScript
9
star
22

quickcache

caching has never been easier
Python
8
star
23

commcare-j2me

J2ME-compatible CommCare. Includes the JavaRosa XForms engine
Java
8
star
24

python-digest

Python
7
star
25

commcare-export

A command-line tool and Python library to generate customized exports from CommCareHQ.
Python
7
star
26

dimagi-utils

Shared utility code used @ Dimagi
Python
6
star
27

tmobilescraper

Scraper for T-Mobile account info when running bulk accounts
Python
6
star
28

FormDesigner

Code for the HQ integrated Form Designer
Java
6
star
29

rd-toolkit

The Rapid Diagnostics Toolkit is a middleware layer for improving the consistency of vision based classifiers and accelerate their rollout
Kotlin
6
star
30

formplayer

Java Spring powered back-end web client for CommCare workflow and persistence engine. Backs "Live Preview" and "Web Apps" features of HQ
Java
6
star
31

sql-agg

Basic framework for doing simple SQL aggregation queries
Python
5
star
32

django-digest

Python
4
star
33

cushions

Collecting XForms data in Couch(DB)
Python
4
star
34

couchdbkit

CouchDB python framework
Python
4
star
35

rapidsms-decisiontree-app

Python
4
star
36

formtranslate

A small django web-api wrapper for JavaRosa's XForm Jar tools
CSS
4
star
37

django-datawarehouse

Simple utility wrapper for doing data warehousing in django
Python
4
star
38

openclinica-xforms

utility code for integrating OpenClinica CRFs and xforms
Python
3
star
39

jmeter

Ansible setup for Jmeter with collectd, InfluxDB and Grafana
Ruby
3
star
40

code-pairing

Python
3
star
41

gpg-scripts

Scripts for managing gpg keyrings and performing common operations
Python
3
star
42

django-soil

An asynchronous downloader for django. Plant your seeds and wait for them to grow!
Python
3
star
43

rapidsms-smscouchforms

Python
3
star
44

commcare-hq-api

Work-in-progress Python library for accessing CommCare HQ API end-points.
Python
3
star
45

couchdb-cluster-admin

utility for managing multi-node couchdb 2.x clusters
Python
3
star
46

pillowtop

A couchdb listening framework to transform and process changes.
Python
2
star
47

dimagi.com

Dimagi.com
HTML
2
star
48

rapidsms-ilsgateway-app

Python
2
star
49

fakecouch

Faster than real couch
Python
2
star
50

couchexport

export couch objects
Python
2
star
51

receiver

The receiver application
Python
2
star
52

rapidsms-groups

Groups for rapidsms
Python
2
star
53

poshan-didi-server

FLASK and Telegram Bot servers for the Poshan Didi project
Python
2
star
54

rapidsms-envaya

Backend for EnvayaSMS Android App
Python
2
star
55

commcare-analytics

CommCareHQ Superset Integration
Python
2
star
56

service-monitor

Python
2
star
57

django-field-audit

Python
2
star
58

rapidsms-alerts

supports web and sms alerts
Python
2
star
59

deploytools

Deploy Tools for Django Projects
Shell
2
star
60

email-reports

generic tool to turn web reports into schedulable, subscribable email reports
Python
2
star
61

carehq

CareHQ Code (Formery ASHand)
Python
2
star
62

rapidsms-reminders

Python
2
star
63

django-scheduler

Scheduler for Django
Python
2
star
64

rapidsms-dupe-checker

A very simple app to auto-respond to duplicate messages in RapidSMS.
Python
2
star
65

_deployment-utils_old

Code for making deployment of a new instance fast and easy (fabric, apache, supervisord, etc scripts)
Python
2
star
66

pyfidelius

ECDSA Key Generation and AES-GCM Encryption/Decryption
Python
2
star
67

rapidsms-groupmessaging

Python
2
star
68

couchdbkit-debugpanel

A debug-toolbar panel for couchdbkit
Python
2
star
69

dimagi-data-platform-R

R code for the aggregate tables and reporting components of Dimagi's internal data platform
R
2
star
70

mWellcare-Printer

Java
1
star
71

public-directory

CSS
1
star
72

commcarehq-prelogin

The site that is available to the public / viewed before a user logs in.
HTML
1
star
73

Salesforce

Salesforce custom apex code
Apex
1
star
74

commcarehq-venv

Python
1
star
75

carehq-mobile

Python
1
star
76

rapidsms-push-backend

Push Backend for Rapidsms
Python
1
star
77

rapidsms-smsforms

An alternative XForms application for RapidSMS.
Python
1
star
78

casexml

The case application
Python
1
star
79

datadog-parsers

log parsers for datadog
Python
1
star
80

Data-Management

All scripts related to Data Management and Quality of Service delivery
Shell
1
star
81

oc-patient-portal

JavaScript
1
star
82

rapidsms-messaging

Messaging for rapidsms
Python
1
star
83

tablib

generic tabular export library
Python
1
star
84

commcare-hq-deploy

CommCareHQ's deploy files
Python
1
star
85

hsph-reports

Reports for HSPH (Better Birth)
Python
1
star
86

djangocouch

utilities for getting data from django to couch
Python
1
star
87

uth-rhd-code

Client side desktop scripts for the UTH RHD Project
HTML
1
star
88

commcare-ios

Swift implementation of the CommCare XForm engine using the FormPlayer API
Swift
1
star
89

sqlcouch

A swap-in replacement for couchdbkit's Document that stores docs using the Django ORM and syncs to couchdb in the background
Python
1
star
90

greenhouse-tools

Some utility code and scripts for extracting data from Greenhouse's Harvest API
Python
1
star
91

ctable_deprecated

[DEPRECATED] CouchDB view to SQL Table
Python
1
star
92

a5288-reports

Python
1
star
93

xep_hq_server

App shared by standalone-xep-hq-server and CommCare HQ for integrating 3rd party XForm Editors
Python
1
star
94

hq-domains

Commcare
Python
1
star
95

mvp-reports

Python
1
star
96

sofabed

Django reporting on couchforms!
Python
1
star
97

commcare-translations

Home to translations of text in CommCare's user interface
Python
1
star
98

openhim-mediator-multitenant

An OpenHIM mediator that supports multiple tenants, each potentially with multiple upstream APIs.
Python
1
star
99

zprintspool

A Zebra Print Job Spooler for the MEPI project
Python
1
star
100

script_yard

Collection of useful and reusable scripts
1
star