按照標准json語法,字符串類型的value是不能換行寫的.
例如,以下是錯誤的寫法
{
"key":"hello
world"
}
但是遇到了需要在json中寫代碼與服務器交互的情況,無奈只能這樣寫:
{
"key":"var a = 1\nvar b = 2\n var c=a+b"
}
代碼行數少了還好,多了極其**,於是想辦法可以在json中換行寫代碼.
嘗試1: json5
JSON5號稱"JSON for Humans",相比標准json主要特點如下:
- 字符串value支持換行
- key可以不加""
- key和value可以使用''代替""
- 支持注釋
- 二進制數值
栗子:
以下是一個合法的json5
{
// comments
unquoted: 'and you can quote me on that',
singleQuotes: 'I can use "double quotes" here',
lineBreaks: "Look, Mom! \
No \\n's!",
hexadecimal: 0xdecaf,
leadingDecimalPoint: .8675309, andTrailing: 8675309.,
positiveSign: +1,
trailingComma: 'in objects', andIn: ['arrays',],
"backwardsCompatible": "with JSON",
}
優點:現成
缺點:
-
換行需要用 \ \標識;
-
解析之后因為沒有了換行,代碼無法執行,所以是達不到目的的;
{
"key":"var a = 1\
var b = 2\
var c=a+b"
}
解析之后的結果為
{
"key":"var a = 1var b = 2var c=a+b"
}
嘗試2: 編寫自己的json解釋器
- 優點: 可根據自己定義的規則解析數據
- 缺點: 成本高.
以下為用python實現的json解釋器,支持換行,目前沒有做完整的錯誤處理,請不要用於生產:
"""
遇到問題沒人解答?小編創建了一個Python學習交流群:778463939
尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書!
Topic: 下降解析器
Desc :
"""
import re
import collections
# Token specification
NUM = r'(?P<NUM>\d+)'
STR_D = r'"(?P<STR_D>[^"]*?)"'
STR_S = r'(?P<STR_S>\'.*?\')'
COLON = r'(?P<COLON>:)'
COMMA = r'(?P<COMMA>,)'
LLB = r'(?P<LLB>\[)'
RLB = r'(?P<RLB>\])'
LDB = r'(?P<LDB>\{)'
RDB = r'(?P<RDB>\})'
WS = r'(?P<WS>\s+)'
NULL = r'(?P<NULL>null)'
FALSE = r'(?P<FALSE>false)'
TRUE = r'(?P<TRUE>true)'
master_pat = re.compile('|'.join([NUM, STR_D, STR_S, LLB, RLB,
LDB, RDB, COLON, COMMA, FALSE, TRUE, NULL, WS]), re.M | re.S)
# Tokenizer
Token = collections.namedtuple('Token', ['type', 'value'])
def generate_tokens(text):
scanner = master_pat.scanner(text)
for m in iter(scanner.match, None):
tok = Token(m.lastgroup, m.group())
if tok.type != 'WS':
yield tok
def simple_join(text):
li = []
for t in generate_tokens(text):
res = t[1]
if t[0] == "STR_S":
res = t[1].replace("'", "\"")
elif t[0].startswith("STR_"):
res = t[1].replace("\n", "\\n").replace("\r\n", "\\n")
li.append(str(res))
return "".join(li)
class Evaluator:
def __init__(self, text):
self.text = text
def parse(self):
self.tokens = generate_tokens(self.text)
self.nexttok = next(self.tokens, None)
self._advance()
return self._parse()
def _advance(self):
'''Advance one token ahead'''
self.tok, self.nexttok = self.nexttok, next(self.tokens, None)
def _parse(self):
if self.tok[0] == "LLB":
return self.get_list()
if self.tok[0] == "LDB":
return self.get_dict()
if self.tok[0].startswith("STR_"):
return self.tok[1].strip('"')
if self.tok[0] == "NUM":
return int(self.tok[1])
if self.tok[0] == "NULL":
return None
if self.tok[0] == "FALSE":
return False
if self.tok[0] == "TRUE":
return True
raise Exception("未知token:{}".format(self.tok[1]))
def get_dict(self):
"""
{ 開頭
中間內容必須為 k:v,
結尾必須為 }
"""
res = {}
self._advance()
# }
if self.tok[0] == "RDB":
return {}
def parse_value():
if not self.tok[0].startswith("STR_"):
raise Exception(f"KEY需是字符串形式,{self.tok[1]}")
new_key = self._parse()
self._advance()
if self.tok[0] != "COLON":
raise Exception("KEY和VALUE需用:分割")
self._advance()
res[new_key] = self._parse()
self._advance()
parse_value()
while self.tok[0] == "COMMA":
self._advance()
parse_value()
return res
def get_list(self):
res = []
self._advance()
# }
if self.tok[0] == "RLB":
return []
def parse_value():
if self.tok[0] in ["NUM", "STR_D", "STR_S", "NULL", "FALSE", "TRUE"]:
res.append(self._parse())
elif self.tok[0] == "LLB":
res.append(self.get_list())
elif self.tok[0] == "LDB":
res.append(self.get_dict())
parse_value()
self._advance()
while self.tok[0] == "COMMA":
self._advance()
parse_value()
self._advance()
return res
if __name__ == '__main__':
text = """
{
"k1":1,
"k2":"v2",
"is_true":true,
"is_none":null,
"k2":"v2",
"k3":{
"a1":"a1",
"a2":"a2",
"code":"
var code = 0
code = 1
"
},
"list":[1,2,3,4]
}
"""
# test(text)
e = Evaluator(text)
res = e.parse()
print(res) #{'k1': 1, 'k2': 'v2', 'is_true': True, 'is_none': None, 'k3': {'a1': 'a1', 'a2': 'a2', 'code': '\n var code = 0\n code = 1\n '}, 'list': [1, 2, 3, 4]}
嘗試3: 全局替換 \n 為 \n
優點:簡單粗暴
{
"key":"
var a = 1
var b = 2
var c=a+b"
}
替換之后的結果為
{
"key":"\nvar a = 1\nvar b = 2\n var c=a+b"
}
缺點:雖然寫的時候有換行,但是上傳到服務器再查詢的時候只能看到\n,依然缺乏可讀性;
嘗試4(最終采用方案):
結合UI將json生成樹狀節點,將代碼類型的value單獨顯示.
優點
- 難度一般
- 按常規方式解析json
- 上傳到服務器再查詢的時候也能看到換行.