SQLite真实数据库: - SQLiteDatabase: execSQL/insert/insertOrThrow/replace/update/delete/query 用sqlite3 - 修复getVersion/setVersion用PRAGMA user_version - 修复getMaximumSize/getPageSize/setMaximumSize/setPageSize用PRAGMA - SQLiteStatement: execute/simpleQueryForLong/simpleQueryForString/execQuery - SQLiteOpenHelper: onCreate/onUpgrade/onDowngrade回调正确触发 - 修复SQLiteCursor.moveToPosition越界处理 - 修复ContentValues值对提取bug JSON真实实现: - JSONObject: isNull/append/putOnce/putOpt/getNames - JSONArray: 完整put/get/opt/length/toString 网络真实实现: - NetworkInterface: getByName/getNetworkInterfaces/getHardwareAddress 用/sys/class/net+fcntl.ioctl - InterfaceAddress: 新增类 - ConnectivityManager: getActiveNetwork/getAllNetworks 用LinuxNetwork - WifiManager: getScanResults用nmcli, setWifiEnabled用nmcli/rfkill - BluetoothAdapter: getAddress/getName 用bluetoothctl 媒体真实实现: - MediaPlayer: prepare检查文件, getCurrentPosition实时计算, getDuration/getVideoWidth用ffprobe - MediaRecorder: setVideoSize/setVideoFrameRate/setVideoEncodingBitRate配置存储 - MediaMetadataRetriever: extractMetadata用ffprobe, getFrameAtTime用ffmpeg, getEmbeddedPicture用ffmpeg 文本真实实现: - TextUtils: split用re.split, concat/indexOf/lastIndexOf/urlEncode/urlDecode - SpannableString: setSpan/getSpans/getSpanFlags/nextSpanTransition 真实实现 - SpannableStringBuilder: 修复append重载bug Provider增强: - Settings: putBoolean/getBoolean - MediaStore: getContentUri/VOLUME常量 - ContactsContract: 补全常量和CommonDataKinds Util增强: - Log: w/e/wtf支持Throwable参数, 修复wtf重复定义bug - TypedValue: 修复complexToDimension/complexToFloat/getFloat实现 修复: - __init__.py: 补全NetworkInterface/InetAddress/MediaMetadataRetriever/SpannableStringBuilder导出 统计: 221模块, 3666类, 31005方法, 183529行
910 lines
27 KiB
Python
910 lines
27 KiB
Python
"""
|
|
Java JSON standard library APIs implemented in Python.
|
|
org.json package - JSONObject, JSONArray, JSONTokener, JSONStringer, JSONWriter, etc.
|
|
"""
|
|
|
|
from .._core import Stub
|
|
import json as _json
|
|
|
|
__all__ = [
|
|
"JSONObject", "JSONArray", "JSONTokener",
|
|
"JSONStringer", "JSONWriter", "JSONException", "JSONPointer",
|
|
"JSONPointerBuilder",
|
|
]
|
|
|
|
|
|
# ============================================================
|
|
# JSONException
|
|
# ============================================================
|
|
|
|
class JSONException(Exception):
|
|
def __init__(self, *args):
|
|
if len(args) == 1 and isinstance(args[0], Exception):
|
|
super().__init__(str(args[0]))
|
|
self._cause = args[0]
|
|
else:
|
|
super().__init__(*args)
|
|
self._cause = None
|
|
|
|
def getCause(self):
|
|
return self._cause
|
|
|
|
|
|
# ============================================================
|
|
# JSONObject.NULL
|
|
# ============================================================
|
|
|
|
class _JSONNull(Stub):
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def equals(self, other):
|
|
return other is None or isinstance(other, _JSONNull)
|
|
|
|
def hashCode(self):
|
|
return 0
|
|
|
|
def toString(self):
|
|
return "null"
|
|
|
|
|
|
# ============================================================
|
|
# JSONObject
|
|
# ============================================================
|
|
|
|
class JSONObject(Stub):
|
|
NULL = _JSONNull()
|
|
|
|
def __init__(self, *args):
|
|
super().__init__()
|
|
self._map = {}
|
|
if len(args) == 1:
|
|
arg = args[0]
|
|
if isinstance(arg, str):
|
|
tokener = JSONTokener(arg)
|
|
obj = tokener.nextValue()
|
|
if isinstance(obj, JSONObject):
|
|
self._map = obj._map
|
|
else:
|
|
raise JSONException("String is not a JSONObject")
|
|
elif isinstance(arg, dict):
|
|
for k, v in arg.items():
|
|
self._map[str(k)] = JSONObject._wrap(v)
|
|
elif isinstance(arg, JSONObject):
|
|
names = None
|
|
self._map = dict(arg._map)
|
|
|
|
def _wrap(val):
|
|
if val is None:
|
|
return JSONObject.NULL
|
|
if isinstance(val, (bool, int, float)):
|
|
return val
|
|
if isinstance(val, str):
|
|
return val
|
|
if isinstance(val, (list, tuple)):
|
|
arr = JSONArray()
|
|
for item in val:
|
|
arr.put(JSONObject._wrap(item))
|
|
return arr
|
|
if isinstance(val, dict):
|
|
obj = JSONObject()
|
|
for k, v in val.items():
|
|
obj.put(str(k), JSONObject._wrap(v))
|
|
return obj
|
|
if isinstance(val, JSONObject) or isinstance(val, JSONArray):
|
|
return val
|
|
return val
|
|
|
|
def put(self, key, value):
|
|
self._map[str(key)] = JSONObject._wrap(value)
|
|
return self
|
|
|
|
def accumulate(self, key, value):
|
|
k = str(key)
|
|
current = self._map.get(k)
|
|
if current is None:
|
|
arr = JSONArray()
|
|
arr.put(value)
|
|
self._map[k] = arr
|
|
elif isinstance(current, JSONArray):
|
|
current.put(value)
|
|
else:
|
|
arr = JSONArray()
|
|
arr.put(current)
|
|
arr.put(value)
|
|
self._map[k] = arr
|
|
return self
|
|
|
|
def append(self, key, value):
|
|
"""Append value to the JSONArray under key.
|
|
|
|
If the key is not present a new JSONArray containing value is created.
|
|
If the key maps to a JSONArray the value is appended to it.
|
|
Otherwise the existing value and the new value are placed into a new
|
|
JSONArray.
|
|
"""
|
|
k = str(key)
|
|
current = self._map.get(k)
|
|
if isinstance(current, JSONArray):
|
|
current.put(value)
|
|
else:
|
|
arr = JSONArray()
|
|
if current is not None and current is not JSONObject.NULL:
|
|
arr.put(current)
|
|
arr.put(value)
|
|
self._map[k] = arr
|
|
return self
|
|
|
|
def putOnce(self, key, value):
|
|
"""Put a key/value pair only if the key is not already present."""
|
|
if key is None:
|
|
raise JSONException("Null key.")
|
|
k = str(key)
|
|
if k not in self._map:
|
|
self._map[k] = JSONObject._wrap(value)
|
|
return self
|
|
|
|
def putOpt(self, key, value):
|
|
"""Put a key/value pair, but only if key and value are both non-null."""
|
|
if key is not None and value is not None:
|
|
return self.put(key, value)
|
|
return self
|
|
|
|
def get(self, key):
|
|
k = str(key)
|
|
if k not in self._map:
|
|
raise JSONException(f"JSONObject[\"{k}\"] not found")
|
|
val = self._map[k]
|
|
if val is JSONObject.NULL:
|
|
raise JSONException(f"JSONObject[\"{k}\"] is null")
|
|
return val
|
|
|
|
def opt(self, key, default=None):
|
|
k = str(key)
|
|
val = self._map.get(k)
|
|
if val is None or val is JSONObject.NULL:
|
|
return default
|
|
return val
|
|
|
|
def getString(self, key):
|
|
val = self.get(key)
|
|
if isinstance(val, str):
|
|
return val
|
|
raise JSONException(f"JSONObject[\"{key}\"] is not a string")
|
|
|
|
def getInt(self, key):
|
|
val = self.get(key)
|
|
if isinstance(val, int):
|
|
return val
|
|
if isinstance(val, float):
|
|
return int(val)
|
|
raise JSONException(f"JSONObject[\"{key}\"] is not an int")
|
|
|
|
def getLong(self, key):
|
|
val = self.get(key)
|
|
if isinstance(val, int):
|
|
return val
|
|
if isinstance(val, float):
|
|
return int(val)
|
|
raise JSONException(f"JSONObject[\"{key}\"] is not a long")
|
|
|
|
def getDouble(self, key):
|
|
val = self.get(key)
|
|
if isinstance(val, (int, float)):
|
|
return float(val)
|
|
raise JSONException(f"JSONObject[\"{key}\"] is not a double")
|
|
|
|
def getBoolean(self, key):
|
|
val = self.get(key)
|
|
if isinstance(val, bool):
|
|
return val
|
|
if val == "true":
|
|
return True
|
|
if val == "false":
|
|
return False
|
|
raise JSONException(f"JSONObject[\"{key}\"] is not a boolean")
|
|
|
|
def getJSONArray(self, key):
|
|
val = self.get(key)
|
|
if isinstance(val, JSONArray):
|
|
return val
|
|
raise JSONException(f"JSONObject[\"{key}\"] is not a JSONArray")
|
|
|
|
def getJSONObject(self, key):
|
|
val = self.get(key)
|
|
if isinstance(val, JSONObject):
|
|
return val
|
|
raise JSONException(f"JSONObject[\"{key}\"] is not a JSONObject")
|
|
|
|
def optString(self, key, default=""):
|
|
val = self.opt(key)
|
|
if val is None or val is JSONObject.NULL:
|
|
return default
|
|
return str(val)
|
|
|
|
def optInt(self, key, default=0):
|
|
val = self.opt(key)
|
|
if val is None or val is JSONObject.NULL:
|
|
return default
|
|
if isinstance(val, int):
|
|
return val
|
|
if isinstance(val, float):
|
|
return int(val)
|
|
try:
|
|
return int(val)
|
|
except (ValueError, TypeError):
|
|
return default
|
|
|
|
def optLong(self, key, default=0):
|
|
return self.optInt(key, default)
|
|
|
|
def optDouble(self, key, default=0.0):
|
|
val = self.opt(key)
|
|
if val is None or val is JSONObject.NULL:
|
|
return default
|
|
if isinstance(val, (int, float)):
|
|
return float(val)
|
|
try:
|
|
return float(val)
|
|
except (ValueError, TypeError):
|
|
return default
|
|
|
|
def optBoolean(self, key, default=False):
|
|
val = self.opt(key)
|
|
if val is None or val is JSONObject.NULL:
|
|
return default
|
|
if isinstance(val, bool):
|
|
return val
|
|
if isinstance(val, str):
|
|
return val.lower() == "true"
|
|
return default
|
|
|
|
def optJSONArray(self, key):
|
|
val = self.opt(key)
|
|
if isinstance(val, JSONArray):
|
|
return val
|
|
return None
|
|
|
|
def optJSONObject(self, key):
|
|
val = self.opt(key)
|
|
if isinstance(val, JSONObject):
|
|
return val
|
|
return None
|
|
|
|
def has(self, key):
|
|
return str(key) in self._map
|
|
|
|
def isNull(self, key):
|
|
"""Returns true if this object has no mapping for key or if it has a
|
|
mapping whose value is JSONObject.NULL."""
|
|
k = str(key)
|
|
if k not in self._map:
|
|
return True
|
|
return self._map[k] is JSONObject.NULL
|
|
|
|
def remove(self, key):
|
|
k = str(key)
|
|
if k in self._map:
|
|
del self._map[k]
|
|
|
|
def keys(self):
|
|
return iter(self._map.keys())
|
|
|
|
def names(self):
|
|
if not self._map:
|
|
return None
|
|
arr = JSONArray()
|
|
for k in self._map:
|
|
arr.put(k)
|
|
return arr
|
|
|
|
@staticmethod
|
|
def getNames(jsonObject):
|
|
"""Returns an array of field names, or None if the object is None/empty."""
|
|
if jsonObject is None:
|
|
return None
|
|
if not jsonObject._map:
|
|
return None
|
|
return [str(k) for k in jsonObject._map.keys()]
|
|
|
|
def length(self):
|
|
return len(self._map)
|
|
|
|
def toString(self, indent_factor=None):
|
|
if indent_factor is None:
|
|
return self._to_json_string()
|
|
return self._to_json_pretty(indent_factor)
|
|
|
|
def _to_json_string(self):
|
|
parts = ["{"]
|
|
for i, (k, v) in enumerate(self._map.items()):
|
|
parts.append('"' + k + '": ' + JSONObject._value_to_string(v))
|
|
if i < len(self._map) - 1:
|
|
parts.append(", ")
|
|
parts.append("}")
|
|
return "".join(parts)
|
|
|
|
def _to_json_pretty(self, indent):
|
|
parts = ["{"]
|
|
for i, (k, v) in enumerate(self._map.items()):
|
|
parts.append("\n" + " " * indent + '"' + k + '": ' + JSONObject._value_to_string(v))
|
|
if i < len(self._map) - 1:
|
|
parts.append(",")
|
|
parts.append("\n}")
|
|
return "".join(parts)
|
|
|
|
def _value_to_string(val):
|
|
if val is None or val is JSONObject.NULL:
|
|
return "null"
|
|
if isinstance(val, bool):
|
|
return "true" if val else "false"
|
|
if isinstance(val, (int, float)):
|
|
return str(val)
|
|
if isinstance(val, str):
|
|
return _json.dumps(val)
|
|
if isinstance(val, JSONObject):
|
|
return val._to_json_string()
|
|
if isinstance(val, JSONArray):
|
|
return val._to_json_string()
|
|
return str(val)
|
|
|
|
def toMap(self):
|
|
result = {}
|
|
for k, v in self._map.items():
|
|
if isinstance(v, JSONObject):
|
|
result[k] = v.toMap()
|
|
elif isinstance(v, JSONArray):
|
|
result[k] = v.toList()
|
|
elif v is JSONObject.NULL:
|
|
result[k] = None
|
|
else:
|
|
result[k] = v
|
|
return result
|
|
|
|
def write(self, writer):
|
|
writer._write(self._to_json_string())
|
|
|
|
def toJSONArray(self, names):
|
|
if names is None or names.length() == 0:
|
|
return None
|
|
arr = JSONArray()
|
|
for i in range(names.length()):
|
|
arr.put(self.opt(names.getString(i)))
|
|
return arr
|
|
|
|
def hashCode(self):
|
|
return hash(frozenset(self._map.items()))
|
|
|
|
def equals(self, other):
|
|
if isinstance(other, JSONObject):
|
|
return self._map == other._map
|
|
return False
|
|
|
|
|
|
# ============================================================
|
|
# JSONArray
|
|
# ============================================================
|
|
|
|
class JSONArray(Stub):
|
|
def __init__(self, *args):
|
|
super().__init__()
|
|
self._list = []
|
|
if len(args) == 1:
|
|
arg = args[0]
|
|
if isinstance(arg, str):
|
|
tokener = JSONTokener(arg)
|
|
val = tokener.nextValue()
|
|
if isinstance(val, JSONArray):
|
|
self._list = val._list
|
|
else:
|
|
raise JSONException("String is not a JSONArray")
|
|
elif isinstance(arg, (list, tuple)):
|
|
for item in arg:
|
|
self.put(JSONObject._wrap(item))
|
|
|
|
def put(self, *args):
|
|
if len(args) == 1:
|
|
self._list.append(JSONObject._wrap(args[0]))
|
|
elif len(args) == 2:
|
|
index, value = args
|
|
while len(self._list) <= index:
|
|
self._list.append(None)
|
|
self._list[index] = JSONObject._wrap(value)
|
|
return self
|
|
|
|
def get(self, index):
|
|
if index < 0 or index >= len(self._list):
|
|
raise JSONException(f"JSONArray[{index}] not found")
|
|
val = self._list[index]
|
|
if val is JSONObject.NULL:
|
|
raise JSONException(f"JSONArray[{index}] is null")
|
|
return val
|
|
|
|
def getString(self, index):
|
|
val = self.get(index)
|
|
if isinstance(val, str):
|
|
return val
|
|
raise JSONException(f"JSONArray[{index}] is not a string")
|
|
|
|
def getInt(self, index):
|
|
val = self.get(index)
|
|
if isinstance(val, int):
|
|
return val
|
|
if isinstance(val, float):
|
|
return int(val)
|
|
raise JSONException(f"JSONArray[{index}] is not an int")
|
|
|
|
def getLong(self, index):
|
|
val = self.get(index)
|
|
if isinstance(val, int):
|
|
return val
|
|
if isinstance(val, float):
|
|
return int(val)
|
|
raise JSONException(f"JSONArray[{index}] is not a long")
|
|
|
|
def getDouble(self, index):
|
|
val = self.get(index)
|
|
if isinstance(val, (int, float)):
|
|
return float(val)
|
|
raise JSONException(f"JSONArray[{index}] is not a double")
|
|
|
|
def getBoolean(self, index):
|
|
val = self.get(index)
|
|
if isinstance(val, bool):
|
|
return val
|
|
if val == "true":
|
|
return True
|
|
if val == "false":
|
|
return False
|
|
raise JSONException(f"JSONArray[{index}] is not a boolean")
|
|
|
|
def getJSONArray(self, index):
|
|
val = self.get(index)
|
|
if isinstance(val, JSONArray):
|
|
return val
|
|
raise JSONException(f"JSONArray[{index}] is not a JSONArray")
|
|
|
|
def getJSONObject(self, index):
|
|
val = self.get(index)
|
|
if isinstance(val, JSONObject):
|
|
return val
|
|
raise JSONException(f"JSONArray[{index}] is not a JSONObject")
|
|
|
|
def opt(self, index, default=None):
|
|
if index < 0 or index >= len(self._list):
|
|
return default
|
|
val = self._list[index]
|
|
if val is JSONObject.NULL:
|
|
return default
|
|
return val
|
|
|
|
def optString(self, index, default=""):
|
|
val = self.opt(index)
|
|
if val is None:
|
|
return default
|
|
return str(val)
|
|
|
|
def optInt(self, index, default=0):
|
|
val = self.opt(index)
|
|
if val is None:
|
|
return default
|
|
if isinstance(val, int):
|
|
return val
|
|
if isinstance(val, float):
|
|
return int(val)
|
|
try:
|
|
return int(val)
|
|
except (ValueError, TypeError):
|
|
return default
|
|
|
|
def optLong(self, index, default=0):
|
|
return self.optInt(index, default)
|
|
|
|
def optDouble(self, index, default=0.0):
|
|
val = self.opt(index)
|
|
if val is None:
|
|
return default
|
|
if isinstance(val, (int, float)):
|
|
return float(val)
|
|
try:
|
|
return float(val)
|
|
except (ValueError, TypeError):
|
|
return default
|
|
|
|
def optBoolean(self, index, default=False):
|
|
val = self.opt(index)
|
|
if val is None:
|
|
return default
|
|
if isinstance(val, bool):
|
|
return val
|
|
if isinstance(val, str):
|
|
return val.lower() == "true"
|
|
return default
|
|
|
|
def optJSONArray(self, index):
|
|
val = self.opt(index)
|
|
if isinstance(val, JSONArray):
|
|
return val
|
|
return None
|
|
|
|
def optJSONObject(self, index):
|
|
val = self.opt(index)
|
|
if isinstance(val, JSONObject):
|
|
return val
|
|
return None
|
|
|
|
def remove(self, index):
|
|
if 0 <= index < len(self._list):
|
|
return self._list.pop(index)
|
|
return None
|
|
|
|
def length(self):
|
|
return len(self._list)
|
|
|
|
def isNull(self, index):
|
|
if 0 <= index < len(self._list):
|
|
return self._list[index] is JSONObject.NULL or self._list[index] is None
|
|
return True
|
|
|
|
def toString(self, indent_factor=None):
|
|
if indent_factor is None:
|
|
return self._to_json_string()
|
|
return self._to_json_pretty(indent_factor)
|
|
|
|
def _to_json_string(self):
|
|
parts = ["["]
|
|
for i, v in enumerate(self._list):
|
|
parts.append(JSONObject._value_to_string(v))
|
|
if i < len(self._list) - 1:
|
|
parts.append(", ")
|
|
parts.append("]")
|
|
return "".join(parts)
|
|
|
|
def _to_json_pretty(self, indent):
|
|
parts = ["["]
|
|
for i, v in enumerate(self._list):
|
|
parts.append("\n" + " " * indent + JSONObject._value_to_string(v))
|
|
if i < len(self._list) - 1:
|
|
parts.append(",")
|
|
parts.append("\n]")
|
|
return "".join(parts)
|
|
|
|
def toList(self):
|
|
result = []
|
|
for v in self._list:
|
|
if isinstance(v, JSONObject):
|
|
result.append(v.toMap())
|
|
elif isinstance(v, JSONArray):
|
|
result.append(v.toList())
|
|
elif v is JSONObject.NULL:
|
|
result.append(None)
|
|
else:
|
|
result.append(v)
|
|
return result
|
|
|
|
def join(self, separator):
|
|
parts = []
|
|
for v in self._list:
|
|
parts.append(JSONObject._value_to_string(v))
|
|
return separator.join(parts)
|
|
|
|
|
|
# ============================================================
|
|
# JSONTokener
|
|
# ============================================================
|
|
|
|
class JSONTokener(Stub):
|
|
def __init__(self, source):
|
|
super().__init__()
|
|
if hasattr(source, "read"):
|
|
self._source = source.read()
|
|
else:
|
|
self._source = str(source)
|
|
self._index = 0
|
|
|
|
def nextValue(self):
|
|
self._skipWhitespace()
|
|
if self._index >= len(self._source):
|
|
raise JSONException("Unexpected end of input")
|
|
ch = self._source[self._index]
|
|
if ch == "{":
|
|
return self._readObject()
|
|
if ch == "[":
|
|
return self._readArray()
|
|
if ch == '"':
|
|
return self._readString()
|
|
if ch == "'":
|
|
return self._readString("'")
|
|
if ch == "t" or ch == "f":
|
|
return self._readLiteral()
|
|
if ch == "n":
|
|
return self._readNull()
|
|
return self._readNumber()
|
|
|
|
def _skipWhitespace(self):
|
|
while self._index < len(self._source) and self._source[self._index] in " \t\n\r":
|
|
self._index += 1
|
|
|
|
def _readObject(self):
|
|
self._index += 1 # skip {
|
|
obj = JSONObject()
|
|
self._skipWhitespace()
|
|
if self._index < len(self._source) and self._source[self._index] == "}":
|
|
self._index += 1
|
|
return obj
|
|
while True:
|
|
self._skipWhitespace()
|
|
key = self.nextValue()
|
|
self._skipWhitespace()
|
|
if self._index < len(self._source) and self._source[self._index] == ":":
|
|
self._index += 1
|
|
self._skipWhitespace()
|
|
val = self.nextValue()
|
|
obj.put(str(key), val)
|
|
self._skipWhitespace()
|
|
if self._index < len(self._source) and self._source[self._index] == ",":
|
|
self._index += 1
|
|
elif self._index < len(self._source) and self._source[self._index] == "}":
|
|
self._index += 1
|
|
return obj
|
|
else:
|
|
raise JSONException("Expected ',' or '}' in object")
|
|
|
|
def _readArray(self):
|
|
self._index += 1 # skip [
|
|
arr = JSONArray()
|
|
self._skipWhitespace()
|
|
if self._index < len(self._source) and self._source[self._index] == "]":
|
|
self._index += 1
|
|
return arr
|
|
while True:
|
|
self._skipWhitespace()
|
|
val = self.nextValue()
|
|
arr.put(val)
|
|
self._skipWhitespace()
|
|
if self._index < len(self._source) and self._source[self._index] == ",":
|
|
self._index += 1
|
|
elif self._index < len(self._source) and self._source[self._index] == "]":
|
|
self._index += 1
|
|
return arr
|
|
else:
|
|
raise JSONException("Expected ',' or ']' in array")
|
|
|
|
def _readString(self, quote='"'):
|
|
self._index += 1 # skip quote
|
|
result = []
|
|
while self._index < len(self._source):
|
|
ch = self._source[self._index]
|
|
if ch == "\\":
|
|
self._index += 1
|
|
if self._index >= len(self._source):
|
|
raise JSONException("Unterminated string escape")
|
|
esc = self._source[self._index]
|
|
if esc == '"':
|
|
result.append('"')
|
|
elif esc == "\\":
|
|
result.append("\\")
|
|
elif esc == "/":
|
|
result.append("/")
|
|
elif esc == "b":
|
|
result.append("\b")
|
|
elif esc == "f":
|
|
result.append("\f")
|
|
elif esc == "n":
|
|
result.append("\n")
|
|
elif esc == "r":
|
|
result.append("\r")
|
|
elif esc == "t":
|
|
result.append("\t")
|
|
elif esc == "u":
|
|
hex_str = self._source[self._index+1:self._index+5]
|
|
result.append(chr(int(hex_str, 16)))
|
|
self._index += 4
|
|
else:
|
|
result.append(esc)
|
|
self._index += 1
|
|
elif ch == quote:
|
|
self._index += 1
|
|
return "".join(result)
|
|
else:
|
|
result.append(ch)
|
|
self._index += 1
|
|
raise JSONException("Unterminated string")
|
|
|
|
def _readLiteral(self):
|
|
for lit in ("true", "false"):
|
|
if self._source[self._index:self._index+len(lit)] == lit:
|
|
self._index += len(lit)
|
|
return lit == "true"
|
|
raise JSONException(f"Unexpected literal at {self._index}")
|
|
|
|
def _readNull(self):
|
|
if self._source[self._index:self._index+4] == "null":
|
|
self._index += 4
|
|
return JSONObject.NULL
|
|
raise JSONException(f"Unexpected token at {self._index}")
|
|
|
|
def _readNumber(self):
|
|
start = self._index
|
|
while self._index < len(self._source) and self._source[self._index] in "-+0123456789.eE":
|
|
self._index += 1
|
|
num_str = self._source[start:self._index]
|
|
if "." in num_str or "e" in num_str or "E" in num_str:
|
|
return float(num_str)
|
|
return int(num_str)
|
|
|
|
def nextString(self, quote='"'):
|
|
return self._readString(quote)
|
|
|
|
def nextChar(self, c):
|
|
self._skipWhitespace()
|
|
if self._index < len(self._source) and self._source[self._index] == c:
|
|
self._index += 1
|
|
return c
|
|
raise JSONException(f"Expected '{c}'")
|
|
|
|
def nextTo(self, delimiter):
|
|
start = self._index
|
|
while self._index < len(self._source) and self._source[self._index] not in delimiter:
|
|
self._index += 1
|
|
return self._source[start:self._index]
|
|
|
|
def skipPast(self, text):
|
|
idx = self._source.find(text, self._index)
|
|
if idx >= 0:
|
|
self._index = idx + len(text)
|
|
|
|
def skipTo(self, c):
|
|
idx = self._source.find(c, self._index)
|
|
if idx >= 0:
|
|
self._index = idx
|
|
return idx
|
|
return -1
|
|
|
|
def back(self):
|
|
self._index -= 1
|
|
|
|
def more(self):
|
|
self._skipWhitespace()
|
|
return self._index < len(self._source)
|
|
|
|
def readString(self):
|
|
return self._readString()
|
|
|
|
|
|
# ============================================================
|
|
# JSONWriter
|
|
# ============================================================
|
|
|
|
class JSONWriter(Stub):
|
|
def __init__(self, writer):
|
|
super().__init__()
|
|
self._writer = writer
|
|
self._stack = []
|
|
self._in_object = False
|
|
self._need_comma = False
|
|
|
|
def _write(self, s):
|
|
self._writer.write(s)
|
|
|
|
def array(self):
|
|
self._beforeValue()
|
|
self._stack.append(("array", False))
|
|
self._write("[")
|
|
return self
|
|
|
|
def endArray(self):
|
|
self._write("]")
|
|
self._stack.pop()
|
|
return self
|
|
|
|
def object(self):
|
|
self._beforeValue()
|
|
self._stack.append(("object", False))
|
|
self._write("{")
|
|
return self
|
|
|
|
def endObject(self):
|
|
self._write("}")
|
|
self._stack.pop()
|
|
return self
|
|
|
|
def key(self, s):
|
|
if not self._stack or self._stack[-1][0] != "object":
|
|
raise JSONException("Can't set key outside object")
|
|
self._beforeKey()
|
|
self._write('"' + s + '":')
|
|
return self
|
|
|
|
def value(self, v):
|
|
self._beforeValue()
|
|
self._write(JSONObject._value_to_string(v))
|
|
return self
|
|
|
|
def _beforeValue(self):
|
|
if self._stack and self._stack[-1][0] == "object":
|
|
raise JSONException("Expected key in object")
|
|
|
|
def _beforeKey(self):
|
|
if self._stack and self._stack[-1][1]:
|
|
self._write(",")
|
|
self._stack[-1] = (self._stack[-1][0], True)
|
|
|
|
|
|
# ============================================================
|
|
# JSONStringer
|
|
# ============================================================
|
|
|
|
class JSONStringer(JSONWriter):
|
|
def __init__(self):
|
|
from io import StringIO
|
|
super().__init__(StringIO())
|
|
|
|
def toString(self):
|
|
return self._writer.getvalue()
|
|
|
|
|
|
# ============================================================
|
|
# JSONPointer
|
|
# ============================================================
|
|
|
|
class JSONPointer(Stub):
|
|
def __init__(self, pointer):
|
|
super().__init__()
|
|
if not pointer or pointer == "":
|
|
self._tokens = []
|
|
elif pointer.startswith("#"):
|
|
pointer = pointer[1:]
|
|
pointer = pointer.replace("~1", "/").replace("~0", "~")
|
|
import urllib.parse
|
|
pointer = urllib.parse.unquote(pointer)
|
|
self._tokens = pointer.split("/")[1:] if len(pointer) > 1 else []
|
|
else:
|
|
self._tokens = pointer.split("/")[1:] if len(pointer) > 0 else []
|
|
|
|
def query(self, root):
|
|
current = root
|
|
for token in self._tokens:
|
|
token = token.replace("~1", "/").replace("~0", "~")
|
|
if isinstance(current, JSONObject):
|
|
current = current.get(token)
|
|
elif isinstance(current, JSONArray):
|
|
try:
|
|
idx = int(token)
|
|
current = current.get(idx)
|
|
except (ValueError, IndexError):
|
|
raise JSONException(f"Invalid index: {token}")
|
|
else:
|
|
raise JSONException(f"Cannot index into {type(current)}")
|
|
return current
|
|
|
|
def queryFrom(self, root):
|
|
return self.query(root)
|
|
|
|
def toString(self):
|
|
return "#/" + "/".join(t.replace("~", "~0").replace("/", "~1") for t in self._tokens)
|
|
|
|
def hashCode(self):
|
|
return hash(tuple(self._tokens))
|
|
|
|
def equals(self, other):
|
|
if isinstance(other, JSONPointer):
|
|
return self._tokens == other._tokens
|
|
return False
|
|
|
|
|
|
# ============================================================
|
|
# JSONPointerBuilder
|
|
# ============================================================
|
|
|
|
class JSONPointerBuilder(Stub):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._tokens = []
|
|
|
|
def append(self, token):
|
|
self._tokens.append(str(token))
|
|
return self
|
|
|
|
def build(self):
|
|
return JSONPointer("#/" + "/".join(t.replace("~", "~0").replace("/", "~1") for t in self._tokens)) |