https://github.com/awinterman/api_object_specification
A DSL for specifying API Objects, and a python module that creates a generator and parser from the spec.
https://github.com/awinterman/api_object_specification
Last synced: 12 months ago
JSON representation
A DSL for specifying API Objects, and a python module that creates a generator and parser from the spec.
- Host: GitHub
- URL: https://github.com/awinterman/api_object_specification
- Owner: AWinterman
- License: apache-2.0
- Created: 2015-04-12T00:17:58.000Z (about 11 years ago)
- Default Branch: master
- Last Pushed: 2021-04-14T23:41:01.000Z (about 5 years ago)
- Last Synced: 2025-01-11T20:34:31.003Z (over 1 year ago)
- Language: Python
- Homepage:
- Size: 83 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.rst
- License: LICENSE
Awesome Lists containing this project
README
API Object Specification
========================
.. contents::
Overview
--------
A DSL for writing specifications of JSON APIs, and an accompanying
implementation written in python. This package generates a parser and generator
for APIs defined by a spec file.
Why not just use a CFG?
-----------------------
CFGs are great!
But JSON APIs often have characteristics that don't match CFGs terribly well.
The DSL actually is a CFG with the following additional characteristics:
- Includes the base JSON types as terminal symbols
- Its JSON object keys are order insensitive: ``{"foo": 1, "bar": 2}`` matches
``{"bar": 2, "foo": 1}`` (This is hard to do with a CFG)
- Is whitespace insensitive (where appropriate).
- Knows when to add and when not to add a comma.
Perhaps more importantly, CFGs are not terribly easy to read.
This is awfully JSON specific!
-------------------------------
You're right! I only write JSON APIs these days, but it doesn't seem
unreasonable to extend the specification to support other formats in the
future.
.. _specification-format:
Specification Format
--------------------
Token Reference
***************
Token references look like ````, where ``NAME`` is any alphanumeric
entry. The ``<`` and ``>`` symbols are configurable.
Each TOKEN reference is replaced by the token definition, if it exists. If no
definition exists, the resulting generator and parser classes will have
corresponding methods which throw ``NotImplemented`` exceptions.
Token Definition
****************
Token definitions look like
::
NAME: DEFINITION
Where the definition is a JSON blob containing token and non-token entries.
Name may be defined multiple times. The resulting specification accepts any
definition of NAME.
If the content type of the api is JSON (configurable), then the following tokens are defined as primitives.
- object
- array
- value
- string
- number
- boolean
Excepting boolean, which is either ``true`` or ``false``, their values are
defined at json.org.
Optional Fields
***************
At current, optional fields are supported simply by specifying two definitions
for the same token name: For example:
::
FOO_WRAPPER: {
"foo": "bar",
"obj":
}
FOO_WRAPPER: {
"foo": "bar",
"array":
}
Repeated Values
***************
``...``
The equivalent of the Kleane star-- the object must have zero or more
repetitions of the proceeding token.
::
STRINGS: [... ]
If there must be at least two strings, then you can say
::
STRINGS: [, , ... ]
Likewise, you can specify any number of keys of a certain type:
::
FLEXIBLE: {
...
}
PAIRS: "narcissus": "man"
PAIRS: "echo": "nymph"
PAIRS: "zues": "god"
Commas will automatically be added where needed.
Implementation
--------------
The JSON parser and generator are implemented according to the following steps:
1. Resolve replacement rules until we have a list of rules which consist of
only literals or terminal symbols. A terminal symbol is a token which either
has no definition, or whose definition is in terms of the primitives defined
above.
2. Construct a list of keypath/terminal symbol pairs for each rule.
3. Check or generate a value for each pair-- that is traverse the candidate
object according to the given keypath, and ensure that it matches the rule's
definition. The ``FLEXIBLE`` example above requires special casing-- the key
can match any of the possibilities defined by the replacement rule.
4. If a regular expression operation rule is used, repeat step (3) until one of the following conditions has been met:
- We have exhausted the regular expression operator.
- The candidate object's entry at the keypath has been consumed.
- We generated a value a configurable maximum number of times.
To Do
-----
- Configuration documentation
- API documentation
- Write a generator which constructs objects according to the data structure described above.
- Write a parser which is configured with the expectations data structure described above,
and takes json objects as input. It should either be a callable or have a method which returns true if an object conforms to
expectations, and false otherwise.