!
.-.
__|=|__
(_/`-`\_) This is Sir Json.
//\___/\\ Or Senior Json.
<>/ \<>
\|_._|/ I can't decide.
<_I_>
||| Either way, he's a fine fellow.
/_|_\
sr json
ASCII credit: Joan G. Stark
WARNING: This is a WIP. Everything subject to ch-ch-ch-changes.
srjson is json with self-referential tags. A small example:
data = {
"a": "<b>/bar",
"b": "foo"
}
result = srjson.loads(data)
print (result)
# {
# "a": "foo/bar",
# "b": "foo"
# }
A tag is any part of a string value that is enclosed in angle brackets. The value within the tag is a lookup into the original json data. In the above example, <b> is a lookup into data['b']. Here's another example:
data = {
"a": "<b.c>/bar",
"b": {
"c": "foo",
"d": "baz"
}
}
result = srjson.loads(data)
print (result)
# {
# "a": "foo/bar",
# "b": {
# "c": "foo",
# "d": "baz"
# }
# }
You can use custom delimiters, rather than angle brackets. To use '%' instead:
data = {
"a": "%b%/bar",
"b": "foo"
}
result = srjson.loads(data, delimiters=('%', '%'))
print (result)
# {
# "a": "foo/bar",
# "b": "foo"
# }
# or: srjson = SRJSON(delimiters=('%', '%'))
If a tag lookup doesn't reference a string value, then an error value is returned:
data = {
"a": "<b>/bar",
"b": {
"c": "foo",
"d": "baz"
}
}
# {
# "a": "|MISSING:b|/bar",
# "b": {
# "c": "foo",
# "d": "baz"
# }
# }
If the lookup value is a number, it is converted to a string:
data = {
"a": "<b>",
"b": 1
}
# data = {
# "a": "1",
# "b": 1
# }
If the lookup value contains tags, those tags are also resolved, ad infinitum:
data = {
"a": "<b>",
"b": "<c><d>",
"c": "x",
"d": "y"
}
# data = {
# "a": "xy",
# "b": "xy",
# "c": "x",
# "d": "y"
# }
Lookups can be into lists and dicts:
data = {
"a": "<b.c.0.x>/bar",
"b": {
"c": [
{
"x": "foo",
"y": "baz"
},
"bar"
]
}
}
# {
# "a": "foo/bar",
# "b": {
# "c": [
# {
# "x": "foo",
# "y": "baz"
# },
# "bar"
# ]
# }
# }
srjson only creates string values (this may change in the future):
data = {
"a": "<b>/bar",
"b": 1
}
# {
# "a": "1",
# "b": 1
# }
srjson has two actions, #PASSWORD and #PROMPT. When encountering these, user action is required to resolve values. srjson comes with a default resolver for these two actions. For #PASSWORD:
data = {
"gmail": "<#PASSWORD>",
}
# user is prompted with "Enter password for 'gmail': ", after which:
# {
# "a": "Somepassword1"
# }
For #PROMPT:
data = {
"name": {
"first": "<#PROMPT>",
"last": "<#PROMPT>"
}
}
# user is prompted with "Enter value for 'name.first': ", after which:
# user is prompted with "Enter value for 'name.last': ", after which:
# data = {
# "name": {
# "first": "Jane",
# "last": "Doe"
# }
# }
You can write your own resolver and plug it in:
srjson.loads(data, resolver=my_resolver)
See srjson.default_resolver for the required function signature.
srjson memorizes previous lookups by flattening the lookup paths:
memo = {
'mysite.password': 'hello'
}
A memo object of the above form can be passed in to loads:
data = {
"mysite": {
"address": "www.foo.com",
"pass": "<#PASSWORD>"
}
}
srjson.loads(data, memo=memo)
# the #PASSWORD action will not need to be resolved, since the memo
# contains the value for the path:
# {
# "mysite": {
# "address": "www.foo.com",
# "pass": "hello"
# }
# }
- srjson must always be valid json
- investigate jsonpath_rw for replacing parser and path expressions
- speed
- simplicity
- refactor resolvers so they are single functions per {action: resolver}
- refactor SRJSON method args into generic **kwargs