Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import argv, stderr
from json import load, dumps
from collections import Sequence
from urllib import unquote
from pprint import pprint
def flee(s):
print >>stderr, s
exit(1)
def loadJSON(p):
try:
with open(str(p), "r") as f:
try:
json = load(f)
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
xlattype = {
"duration": "timedelta",
"object": "document",
"number": "real",
}
def process_simple(sch, name, parent, ordlist, card, whole, res):
"""
Generate Mentat schema entry
"""
# If IDEA type referenced, use that as type name, otherwise
# use basic type, lowercased, possibly renamed.
if "$ref" in sch and sch["$ref"].startswith("#/definitions/"):
mytype = sch["$ref"].split("/")[-1]
else:
mytype = sch["type"]
mytype = mytype.lower()
if mytype in xlattype:
mytype = xlattype[mytype]
# Populate basic Mentat schema data
new = {
"type": mytype,
"ordinality": "mandatory" if name is not None and name in ordlist else "optional",
"cardinality": "multi" if card else "single",
"description": sch["description"],
}
if "enum" in sch:
new["values"] = sch["enum"]
if parent:
new["parents"] = [parent]
# If same name entries exist, create new
# Should create only if data differ, but that would need some nitpicky
# comparison dancing on all candidates, so we just create duplicates
# in any case of clash
newname = name
while newname in res:
new["name"] = name
newname = newname + "A"
res[newname] = new
def recurse(sch, name, parent, ordlist, card, whole, 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, res)
else:
flee("Basic type not defined: %s", pprint(schema))
def process_object(sch, name, parent, ordlist, card, whole, 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, 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, res)
def process_array(sch, name, parent, ordlist, card, whole, res):
"""
Process array type (recurse just for one "items" children, but indicate
cardinality
"""
recurse(sch["items"], name, parent, ordlist, True, whole, res)
fork = {
"object": process_object,
"array": process_array,
"string": process_simple,
"number": process_simple,
"integer": process_simple,
"boolean": process_simple
}
def main():
if len(argv)==2:
schp = argv[1]
else:
flee("Usage: %s <jsonschemafile>" % split(argv[0])[-1])
schema = loadJSON(schp)
res = {}
# First structure of JSON schema must be object
process_object(schema, None, "", [""], False, schema, res)
print dumps(res, indent=8, sort_keys=True)
main()