json ; .
, json, , tokenize module:
import tokenize
from io import BytesIO
def inline_special(json_data):
def adjust(t, ld,):
"""Adjust token line number by offset"""
(sl, sc), (el, ec) = t.start, t.end
return t._replace(start=(sl + ld, sc), end=(el + ld, ec))
def transform():
with BytesIO(json_data.encode('utf8')) as b:
held = []
lastend = None
loffset = 0
tokens = tokenize.tokenize(b.readline)
for tok in tokens:
if tok.type == tokenize.NL:
held.append(adjust(tok, loffset))
elif (tok.type == tokenize.STRING and
tok.string == '"$special"'):
loffset -= len(held)
held = []
text = [tok.string]
while tok.exact_type != tokenize.RBRACE:
tok = next(tokens)
if tok.type != tokenize.NL:
text.append(tok.string)
if tok.string in ':,':
text.append(' ')
else:
loffset -= 1
line, col = lastend
text = ''.join(text)
endcol = col + len(text)
yield tokenize.TokenInfo(
tokenize.STRING, text, (line, col), (line, endcol),
'')
while tok.type != tokenize.NL:
tok = next(tokens)
yield tok._replace(
start=(line, endcol),
end=(line, endcol + len(tok.string)))
endcol += len(tok.string)
else:
if held:
yield from held
held = []
tok = adjust(tok, loffset)
lastend = tok.end
yield tok
return tokenize.untokenize(transform()).decode('utf8')
:
import json
data = {
'x': [1, {'$special': 'a'}, 2],
'y': {'$special': 'b'},
'z': {'p': True, 'q': False}
}
>>> print(inline_special(json.dumps(data, indent=2)))
{
"x": [
1,
{"$special": "a"},
2
],
"y": {"$special": "b"},
"z": {
"p": true,
"q": false
}
}