在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:Onyo/jsonbender开源软件地址:https://github.com/Onyo/jsonbender开源编程语言:Python 100.0%开源软件介绍:AboutJSONBender is an embedded Python DSL for transforming dicts. It's name is inspired by Nickelodeon's cartoon series Avatar: The Last Airbender. LicenseJSONBender is licensed under the MIT license. See the LICENSE file for more details. Installingpip install JSONBender ContributingIf you want to contribute to JSONBender (thanks!), here's how to do it:
tox tests
UsageJSONBender works by calling the The mapping itself is a dict whose values are benders, i.e. objects that represent the transformations to be done to the source dict. Ex: import json
from jsonbender import bend, K, S
MAPPING = {
'fullName': (S('customer', 'first_name') +
K(' ') +
S('customer', 'last_name')),
'city': S('address', 'city'),
}
source = {
'customer': {
'first_name': 'Inigo',
'last_name': 'Montoya',
'Age': 24,
},
'address': {
'city': 'Sicily',
'country': 'Florin',
},
}
result = bend(MAPPING, source)
print(json.dumps(result)) {"city": "Sicily", "fullName": "Inigo Montoya"} BendersSelectorsK
S
from jsonbender import bend, S
MAPPING = {'val': S('a', 'deeply', 'nested', 0, 'value')}
ret = bend(MAPPING, {'a': {'deeply': {'nested': [{'value': 42}]}}})
assert ret == {'val': 42} If any of keys may not exist, OptionalS
from jsonbender import bend, OptionalS
source = {'does': {'exist': 23}}
MAPPING_1 = {'val': OptionalS('does', 'not', 'exist')}
ret = bend(MAPPING_1, source)
assert ret == {'val': None}
MAPPING_2 = {'val': OptionalS('does', 'not', 'exist', default=27)}
ret = bend(MAPPING_2, source)
assert ret == {'val': 27} For readability and reusability, prefer using F
The extra optional args and kwargs are passed to the function at bending time after the given value. from jsonbender import bend, F, S
MAPPING = {
'total_number_of_keys': F(len),
'number_of_str_keys': F(lambda source: len([k for k in source.keys()
if isinstance(k, str)])),
'price_truncated': S('price_as_str') >> F(float) >> F(int),
}
ret = bend(MAPPING, {'price_as_str': '42.2', 'k1': 'v', 1: 'a'})
assert ret == {'price_truncated': 42,
'total_number_of_keys': 3,
'number_of_str_keys': 2} If the function can't take certain values, you can protect it by calling the import math
from jsonbender import bend, F, S
MAPPING_1 = {'sqrt': S('val') >> F(math.sqrt).protect()}
assert bend(MAPPING_1, {'val': 4}) == {'sqrt': 2}
assert bend(MAPPING_1, {'val': None}) == {'sqrt': None}
MAPPING_2 = {'sqrt': S('val') >> F(math.sqrt).protect(-1)}
assert bend(MAPPING_2, {'val': -1}) == {'sqrt': -1} OperatorsBenders implement most of python's binary operators. ArithmeticFor the arithmetic from jsonbender import bend, K, S
a = S('a')
b = S('b')
MAPPING = {'add': a + b, 'sub': a - b, 'mul': a * b, 'div': a / b}
ret = bend(MAPPING, {'a': 10, 'b': 5})
assert ret == {'add': 15, 'sub': 5, 'mul': 50, 'div': 2}
ret = bend({'full_name': S('first_name') + K(' ') + S('last_name')},
{'first_name': 'John', 'last_name': 'Doe'})
assert ret == {'full_name': 'John Doe'} BitwiseThe bitwise operators are not yet implemented, except for the lshift ( List opsThere are 4 benders for working with lists, inspired by the common functional programming operations. ReduceSimilar to Python's from jsonbender import bend, Reduce, S
MAPPING = {'sum': S('ints') >> Reduce(lambda acc, i: acc + i)}
ret = bend(MAPPING, {'ints': [1, 4, 7, 9]})
assert ret == {'sum': 21} FilterSimilar to Python's from jsonbender import bend, Filter, S
MAPPING = {'even': S('ints') >> Filter(lambda i: i % 2 == 0)}
ret = bend(MAPPING, {'ints': range(5)})
assert ret == {'even': [0, 2, 4]} ForallSimilar to Python's from jsonbender import bend, Forall, S
MAPPING = {'doubles': S('ints') >> Forall(lambda i: i * 2)}
ret = bend(MAPPING, {'ints': range(5)})
assert ret == {'doubles': [0, 2, 4, 6, 8]} For the common case of applying a JSONBender mapping to each element of a list,
the ForallBendBends each element of the list with given mapping and context. If no context is passed, it "inherits" at bend-time the context passed to the outer from jsonbender import bend, S
from jsonbender.list_ops import ForallBend
MAPPING = {'list_of_bs': S('list_of_as') >> ForallBend({'b': S('a')})}
source = {'list_of_as': [{'a': 23}, {'a': 27}]}
ret = bend(MAPPING, source)
assert ret == {'list_of_bs': [{'b': 23}, {'b': 27}]} FlatForallSimilar to Forall, but the given function must return an iterable for each element of the iterable, which are than "flattened" into a single list. from jsonbender import bend, S
from jsonbender.list_ops import FlatForall
MAPPING = {'doubles_triples': S('ints') >> FlatForall(lambda x: [x * 2, x * 3])}
source = {'ints': [2, 15, 50]}
ret = bend(MAPPING, source)
assert ret == {'doubles_triples': [4, 6, 30, 45, 100, 150]} Control FlowSometimes what bender to use must be decided at bending time, so JSONBender provides 3 control flow structures: AlternationTake any number of benders, and return the value of the first one that doesn't raise a LookupError (KeyError, IndexError etc.). If all benders raise LookupError, re-raise the last raised exception. from jsonbender import S
from jsonbender.control_flow import Alternation
b = Alternation(S(1), S(0), S('key1'))
b(['a', 'b']) # -> 'b'
b(['a']) # -> 'a'
try:
b([]) # -> TypeError
except TypeError:
pass
try:
b({}) # -> KeyError
except KeyError:
pass
b({'key1': 23}) # -> 23 IfTakes a condition bender, and two benders (both default to K(None)). If the condition bender evaluates to true, return the value of the first bender. If it evaluates to false, return the value of the second bender. from jsonbender import K, S
from jsonbender.control_flow import If
if_ = If(S('country') == K('China'), S('first_name'), S('last_name'))
if_({'country': 'China',
'first_name': 'Li',
'last_name': 'Na'}) # -> 'Li'
if_({'country': 'Brazil',
'first_name': 'Gustavo',
'last_name': 'Kuerten'}) # -> 'Kuerten' SwitchTake a key bender, a 'case' container of benders and a default bender (optional). The value returned by the key bender is used to get a bender from the case container, which then returns the result. If the key is not in the case container, the default is used. If it's unavailable, raise the original LookupError. from jsonbender import K, S
from jsonbender.control_flow import Switch
b = Switch(S('service'),
{'twitter': S('handle'),
'mastodon': S('handle') + K('@') + S('server')},
default=S('email'))
b({'service': 'twitter', 'handle': 'etandel'}) # -> 'etandel'
b({'service': 'mastodon', 'handle': 'etandel',
'server': 'mastodon.social'}) # -> '[email protected]'
b({'service': 'facebook',
'email': '[email protected]'}) # -> '[email protected]' String opsJSONBender currently provides only one string-related bender. FormatReturn a formatted string just like It uses the same syntax as from jsonbender import bend, Format, S
MAPPING = {'formatted': Format('{} {} {last}',
S('first'),
S('second'),
last=S('last'))}
source = {'first': 'Edsger', 'second': 'W.', 'last': 'Dijkstra'}
ret = bend(MAPPING, source)
assert ret == {'formatted': 'Edsger W. Dijkstra'} CompositionAll JSONBenders can be composed with other benders using from jsonbender import bend, F, S
from jsonbender.list_ops import Forall
MAPPING = {
'name': S('name'),
'pythonista': S('prog_langs') >> Forall(str.lower) >> F(lambda ls: 'python' in ls),
}
source = {
'name': 'Mary',
'prog_langs': ['C', 'Python', 'Lua'],
}
ret = bend(MAPPING, source)
assert ret == {'name': 'Mary', 'pythonista': True} ContextSometimes it's necessary to use values at bending time that are not on the
source json and are not known at mapping time.
For these cases there is the optional from jsonbender import bend, Context, S
MAPPING = {
'name': S('name'),
'age': (Context() >> S('year')) - S('birthyear'),
}
source = {'name': 'Mary', 'birthyear': 1990}
ret = bend(MAPPING, source, context={'year': 2016})
assert ret == {'name': 'Mary', 'age': 26} |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论