2019-11-01 22:33:28 +03:00
|
|
|
# Portions Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
#
|
|
|
|
# This software may be used and distributed according to the terms of the
|
|
|
|
# GNU General Public License version 2.
|
|
|
|
|
2018-07-09 20:18:02 +03:00
|
|
|
# json.py - json encoding
|
|
|
|
#
|
2019-11-01 22:33:28 +03:00
|
|
|
# Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
|
2018-07-09 20:18:02 +03:00
|
|
|
#
|
|
|
|
# This software may be used and distributed according to the terms of the
|
|
|
|
# GNU General Public License version 2 or any later version.
|
|
|
|
|
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2018-08-23 07:26:48 +03:00
|
|
|
import json as _sysjson
|
2020-01-31 21:53:21 +03:00
|
|
|
import sys
|
2018-08-23 07:26:48 +03:00
|
|
|
|
2019-01-30 03:25:33 +03:00
|
|
|
from edenscm.mercurial import encoding, error, pycompat, util
|
2018-07-09 20:18:02 +03:00
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
long
|
|
|
|
except NameError:
|
|
|
|
long = int
|
|
|
|
|
|
|
|
|
|
|
|
def dumps(obj, paranoid=True):
|
|
|
|
if obj is None:
|
|
|
|
return "null"
|
|
|
|
elif obj is False:
|
|
|
|
return "false"
|
|
|
|
elif obj is True:
|
|
|
|
return "true"
|
|
|
|
elif isinstance(obj, (int, long, float)):
|
|
|
|
return pycompat.bytestr(obj)
|
|
|
|
elif isinstance(obj, bytes):
|
|
|
|
return '"%s"' % encoding.jsonescape(obj, paranoid=paranoid)
|
|
|
|
elif isinstance(obj, str):
|
2020-01-29 18:38:41 +03:00
|
|
|
return _sysjson.dumps(obj)
|
2018-07-09 20:18:02 +03:00
|
|
|
elif util.safehasattr(obj, "keys"):
|
2020-02-01 05:27:03 +03:00
|
|
|
out = []
|
|
|
|
for k, v in sorted(pycompat.iteritems(obj)):
|
|
|
|
if isinstance(k, bytes):
|
|
|
|
key = '"%s"' % encoding.jsonescape(k, paranoid=paranoid)
|
|
|
|
else:
|
|
|
|
key = _sysjson.dumps(k)
|
|
|
|
out.append(key + ": %s" % dumps(v, paranoid))
|
2018-07-09 20:18:02 +03:00
|
|
|
return "{" + ", ".join(out) + "}"
|
|
|
|
elif util.safehasattr(obj, "__iter__"):
|
|
|
|
out = [dumps(i, paranoid) for i in obj]
|
|
|
|
return "[" + ", ".join(out) + "]"
|
|
|
|
else:
|
|
|
|
raise TypeError("cannot encode type %s" % obj.__class__.__name__)
|
2018-08-23 07:26:48 +03:00
|
|
|
|
|
|
|
|
2020-02-28 22:29:36 +03:00
|
|
|
def dump(data, fp):
|
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
return fp.write(dumps(data))
|
|
|
|
else:
|
|
|
|
return _sysjson.dump(data, fp)
|
|
|
|
|
|
|
|
|
2018-08-23 07:26:48 +03:00
|
|
|
def _rapply(f, xs):
|
|
|
|
if xs is None:
|
|
|
|
# assume None means non-value of optional data
|
|
|
|
return xs
|
|
|
|
if isinstance(xs, (list, set, tuple)):
|
|
|
|
return type(xs)(_rapply(f, x) for x in xs)
|
|
|
|
if isinstance(xs, dict):
|
|
|
|
return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items())
|
|
|
|
return f(xs)
|
|
|
|
|
|
|
|
|
2020-02-11 20:36:57 +03:00
|
|
|
def load(fp):
|
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
return loads(fp.read())
|
|
|
|
else:
|
|
|
|
return _sysjson.load(fp)
|
|
|
|
|
2020-05-07 19:05:16 +03:00
|
|
|
|
2018-08-23 07:26:48 +03:00
|
|
|
def loads(string):
|
|
|
|
"""Like stdlib json.loads, but results are bytes instead of unicode
|
|
|
|
|
|
|
|
Warning: this does not round-trip with "dumps". "dumps" supports non-utf8
|
|
|
|
binary content that is unsupported by this function.
|
|
|
|
"""
|
2020-01-31 21:53:21 +03:00
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
# XXX: This should round-trip with "dumps". But it might be non-trivial to
|
|
|
|
# do so.
|
2020-02-04 05:24:33 +03:00
|
|
|
def encode(s):
|
|
|
|
if isinstance(s, type(u"")):
|
|
|
|
return pycompat.decodeutf8(s.encode("utf-8"))
|
|
|
|
else:
|
|
|
|
return s
|
|
|
|
|
|
|
|
return _rapply(encode, _sysjson.loads(string))
|
2020-01-31 21:53:21 +03:00
|
|
|
else:
|
|
|
|
return _sysjson.loads(string)
|