Skip to content

Commit

Permalink
Merge pull request #4 from ebi-ait/feature/sub-mapping
Browse files Browse the repository at this point in the history
Resolve spec inside  and json_array and json_object
  • Loading branch information
ke4 authored Feb 12, 2021
2 parents 9da15ad + 4fc79fd commit b9fec41
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 14 deletions.
7 changes: 7 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
language: python
python:
- 3.7
install:
- pip install -r requirements-dev.txt
script:
- nosetests tests -v
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,8 @@ For example:
'institution': 'Some Research Institute'
}
]]


#### Convenience Methods
```
from json_converter.json_mapper import json_object, json_array
Expand Down Expand Up @@ -377,3 +378,70 @@ The `json_array` method treats argument list as a list of JSON objects. For exam
Note that any list literal provided within the `json_array` method is treated as a single object. For instance, the
call `json_array([{'object_id': 123}, {'object_id': 456}])` has *one* item in the resulting list of list.


#### More complex objects inside $array and $object

The object inside the $array or $object spec can now have values which can be sourced from the source object if the third boolean parameter boolean (`contains_spec`) is set to True

Given the following source object:
```
source_object = {
'name': 'Peter Z',
'institution': 'Some University',
'address': 'Some place'
}
```

Using the mapping:

```
from json_converter.json_mapper import JsonMapper
from json_converter.post_process import default_to
json_mapper = JsonMapper(source_object)
values = [
{
'key': ['', default_to, 'name'],
'value': ['name']
},
{
'key': ['', default_to, 'institution'],
'value': ['institution']
},
{
'key': ['', default_to, 'address'],
'value': ['address']
},
]
result = json_mapper.map({
'attributes': ['$array', values, True]
})
```


```
# result contains
{
'attributes':[
{
'key': 'name',
'value': 'Peter Z'
},
{
'key': 'institution',
'value': 'Some University'
},
{
'key': 'address',
'value': 'Some place'
}
]
}
```
25 changes: 18 additions & 7 deletions json_converter/json_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,10 @@ def _passes(filter_spec: list, node: DataNode):
passing = bool(do_filter(*filter_args))
return passing

@staticmethod
def _apply_field_spec(node: DataNode, spec: list):
def _apply_field_spec(self, node: DataNode, spec: list):
source_field_name = spec[0]
if source_field_name in [SPEC_OBJECT_LITERAL, SPEC_ARRAY_LITERAL]:
field_value = JsonMapper._get_object_literal(spec)
field_value = self._get_object_literal(spec)
else:
field_value = node.get(source_field_name)
has_customisation = len(spec) > 1
Expand All @@ -114,15 +113,27 @@ def _apply_field_spec(node: DataNode, spec: list):
field_value = operation(*args)
return field_value

@staticmethod
def _get_object_literal(spec):
if len(spec) != 2:
raise UnreadableSpecification('Expecting exactly 1 JSON literal value.')
def _get_object_literal(self, spec):
if len(spec) < 2 or len(spec) > 3:
raise UnreadableSpecification(f'The {spec[0]} spec can either have 1 or 2 parameters.')

field_value = spec[1]

if not (isinstance(field_value, Mapping) or isinstance(field_value, list)):
raise UnreadableSpecification('JSON literal should be a dict-like or list structure.')

contains_spec = spec[2] if len(spec) == 3 else False

if len(field_value) == 0:
field_value = None

if contains_spec and isinstance(field_value, list):
for i, item in enumerate(field_value):
field_value[i] = self.map(item)

if contains_spec and isinstance(field_value, Mapping):
field_value = self.map(field_value)

return field_value


Expand Down
9 changes: 3 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import setuptools

# install_requires = [line.rstrip() for line in open('requirements.txt')]

with open("README.md", "r", encoding="utf-8") as fh:
long_description = fh.read()

setuptools.setup(
name="json-converter",
version="0.3.0",
description="json-converter is a tool for translating/converting a JSON document into another JSON document with a different structure.",
version="0.4.0",
description="json-converter is a tool for translating/converting a JSON document into another JSON document "
"with a different structure.",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/ebi-ait/json-converter",
packages=setuptools.find_packages(exclude=['tests', 'tests.*']),
# install_requires=install_requires,
# include_package_data=True,
classifiers=[
"Development Status :: 3 - Alpha",
"Programming Language :: Python :: 3",
Expand Down
57 changes: 57 additions & 0 deletions tests/test_json_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from unittest import TestCase

from json_converter.json_mapper import JsonMapper, InvalidNode, UnreadableSpecification
from json_converter.post_process import default_to


class JsonMapperTest(TestCase):
Expand Down Expand Up @@ -417,6 +418,33 @@ def test_map_with_object_literal(self):
self.assertEqual(metadata, result.get('metadata'))
self.assertFalse('empty' in result.keys())

def test_map_with_object_with_spec(self):
# given:
json_mapper = JsonMapper({
'from_key': 'from_value'
})

# when:
values = {
'obj_with_spec': {
'new_key': ['from_key']
}
}
result = json_mapper.map({
'metadata': ['$object', values, True],
'empty': ['$object', {}]
})

expected_value = {
'obj_with_spec': {
'new_key': 'from_value'
}
}

# then:
self.assertEqual(expected_value, result.get('metadata'))
self.assertFalse('empty' in result.keys())

def test_map_with_invalid_object_literal(self):
# given:
json_mapper = JsonMapper({})
Expand Down Expand Up @@ -447,3 +475,32 @@ def test_map_with_array_literal(self):
# then:
self.assertEqual(values, result.get('metadata'))
self.assertFalse('empty' in result.keys())

def test_map_with_array_object_with_spec(self):
# given:
json_mapper = JsonMapper({
'from_key': 'from_value'
})

# when:
values = [
{
'name': ['', default_to, 'name'],
'value': ['from_key']
}
]
result = json_mapper.map({
'metadata': ['$array', values, True],
'empty': ['$array', []]
})

expected_value = [
{
'name': 'name',
'value': 'from_value'
}
]

# then:
self.assertEqual(expected_value, result.get('metadata'))
self.assertFalse('empty' in result.keys())

0 comments on commit b9fec41

Please sign in to comment.