# Copyright 2008-2015 Nokia Networks
# Copyright 2016- Robot Framework Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[docs]
class JsonWriter:
def __init__(self, output, separator=''):
self._writer = JsonDumper(output)
self._separator = separator
[docs]
def write_json(self, prefix, data, postfix=';\n', mapping=None,
separator=True):
self._writer.write(prefix)
self._writer.dump(data, mapping)
self._writer.write(postfix)
self._write_separator(separator)
[docs]
def write(self, string, postfix=';\n', separator=True):
self._writer.write(string + postfix)
self._write_separator(separator)
def _write_separator(self, separator):
if separator and self._separator:
self._writer.write(self._separator)
[docs]
class JsonDumper:
def __init__(self, output):
self.write = output.write
self._dumpers = (MappingDumper(self),
IntegerDumper(self),
TupleListDumper(self),
StringDumper(self),
NoneDumper(self),
DictDumper(self))
[docs]
def dump(self, data, mapping=None):
for dumper in self._dumpers:
if dumper.handles(data, mapping):
dumper.dump(data, mapping)
return
raise ValueError('Dumping %s not supported.' % type(data))
class _Dumper:
_handled_types = None
def __init__(self, jsondumper):
self._dump = jsondumper.dump
self._write = jsondumper.write
def handles(self, data, mapping):
return isinstance(data, self._handled_types)
def dump(self, data, mapping):
raise NotImplementedError
[docs]
class StringDumper(_Dumper):
_handled_types = str
_search_and_replace = [('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'),
('\n', '\\n'), ('\r', '\\r'), ('</', '\\x3c/')]
[docs]
def dump(self, data, mapping):
self._write('"%s"' % (self._escape(data) if data else ''))
def _escape(self, string):
for search, replace in self._search_and_replace:
if search in string:
string = string.replace(search, replace)
return string
[docs]
class IntegerDumper(_Dumper):
# Handles also bool
_handled_types = int
[docs]
def dump(self, data, mapping):
self._write(str(data).lower())
[docs]
class DictDumper(_Dumper):
_handled_types = dict
[docs]
def dump(self, data, mapping):
write = self._write
dump = self._dump
write('{')
last_index = len(data) - 1
for index, key in enumerate(sorted(data)):
dump(key, mapping)
write(':')
dump(data[key], mapping)
if index < last_index:
write(',')
write('}')
[docs]
class TupleListDumper(_Dumper):
_handled_types = (tuple, list)
[docs]
def dump(self, data, mapping):
write = self._write
dump = self._dump
write('[')
last_index = len(data) - 1
for index, item in enumerate(data):
dump(item, mapping)
if index < last_index:
write(',')
write(']')
[docs]
class MappingDumper(_Dumper):
[docs]
def handles(self, data, mapping):
try:
return mapping and data in mapping
except TypeError:
return False
[docs]
def dump(self, data, mapping):
self._write(mapping[data])
[docs]
class NoneDumper(_Dumper):
[docs]
def handles(self, data, mapping):
return data is None
[docs]
def dump(self, data, mapping):
self._write('null')