Skip to content
Snippets Groups Projects
Commit cff41a3a authored by Pavel Kácha's avatar Pavel Kácha
Browse files

Generator of human readable definition from JSON Schema descriptions

parent a463f5cf
Branches
No related tags found
No related merge requests found
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import argv, stderr
from json import load, dumps
from collections import Sequence, OrderedDict
from urllib import unquote
from pprint import pprint
import re
def flee(s):
print >>stderr, s
exit(1)
def loadJSON(p):
try:
with open(str(p), "r") as f:
try:
json = load(f, object_pairs_hook=OrderedDict)
except ValueError, err:
flee ("%s: %s" % (p, err))
except IOError, err:
flee(err)
return json
def resolve_ref(document, fragment):
"""
Resolve $ref. Only local relative URIs are supported.
"""
fragment = unquote(fragment).lstrip("#").lstrip("/")
parts = fragment.split("/") if fragment else []
for part in parts:
part = part.replace("~1", "/").replace("~0", "~")
if isinstance(document, Sequence):
# Array indexes should be turned into integers
try:
part = int(part)
except ValueError:
pass
try:
document = document[part]
except (TypeError, LookupError):
flee("Unresolvable JSON pointer: %r" % fragment)
return document
def is_camel_cased(s):
return re.search('[a-z].*[A-Z].*[a-z]', s) is not None
def process_simple(sch, name, parent, ordlist, card, whole, indent, res):
"""
Generate Moin format entry
"""
if "$ref" in sch and sch["$ref"].startswith("#/definitions/"):
mytype = sch["$ref"].split("/")[-1]
mytype = u"[[#" + mytype + u"|" + mytype + u"]]"
else:
mytype = sch["type"]
mytype = u"''" + mytype + u"''"
res["def"].append(
u"%(indent)s''%(nocamel)s%(name)s'': %(card)s%(type)s\n%(indent)s\t%(desc)s\n" % {
"indent": u"\t" * indent,
"name": name,
"type": mytype,
"card": u"array of " if card else u"",
"desc": sch["description"],
"nocamel": u"!" if is_camel_cased(name) else u""
}
)
def recurse(sch, name, parent, ordlist, card, whole, indent, res):
"""
Recurse into subtrees based on type.
Also crudely resolves references (by merging into current tree).
"""
# Crudely resolve references (by merging into current tree)
if "$ref" in sch:
ref = resolve_ref(whole, sch["$ref"])
sch = dict(ref.items() + sch.items())
# Jumptable recursion
if "type" in sch:
fork[sch["type"]](sch, name, parent, ordlist, card, whole, indent, res)
else:
flee("Basic type not defined: %s", pprint(schema))
def process_object(sch, name, parent, ordlist, card, whole, indent, res):
"""
Process object type and recurse for children
"""
# Generate data for non root objects only
if name:
process_simple(sch, name, parent, ordlist, card, whole, indent, res)
# Set ordinality list for direct subobject of this object
neword = sch["required"] if "required" in sch else []
# Process each child, with False cardinality
for n, subsch in sch["properties"].iteritems():
recurse(subsch, n, name, neword, False, whole, indent+1, res)
def process_array(sch, name, parent, ordlist, card, whole, indent, res):
"""
Process array type (recurse just for one "items" children, but indicate
cardinality
"""
recurse(sch["items"], name, parent, ordlist, True, whole, indent, res)
fork = {
"object": process_object,
"array": process_array,
"string": process_simple,
"number": process_simple,
"integer": process_simple,
"boolean": process_simple
}
def process_types(schema, res):
"""
Process Types section
"""
for t, vals in schema["definitions"].iteritems():
res["types"].append(
u"<<Anchor(%(name)s)>>\n=== %(name)s ===\n\n%(desc)s\n\n" % {
"name": t,
"desc": vals["description"]
}
)
def main():
if len(argv)==2:
schp = argv[1]
else:
flee("Usage: %s <jsonschemafile>" % split(argv[0])[-1])
schema = loadJSON(schp)
res = {
"def": [],
"types": []
}
# First structure of JSON schema must be object
process_object(schema, None, "", [""], False, schema, -1, res)
process_types(schema, res)
print u"".join(
[schema["description"]] +
[u"\n\n== Definition ==\n\n"] +
res["def"] +
[u"\n<<Anchor(Types)>>\n== Types ==\n\n"] +
res["types"]
).encode("utf-8")
main()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment