Configuration Json data¶
The configuration governs not only where to find data, but also the structure of the output which will mirror the structure in the configuration json.
The two main components of the configuration json is the object and attributes. An object can contain nested objects and/or attributes. In the attribute part of the file is where you actually tell the mapper where to find data. In the object you are deciding the structure and also telling the mapper if there are iterable data anywhere that needs to be iterated to create multiple instances.
Object¶
An object has a name, it can have attributes, nested objects or a special type of objects called branching objects. It will also know if itself is an array and the path to where the input data can be iterated to create multiple objects.
name | type | description | comment |
---|---|---|---|
name | str | name of the key it will get in parent object | the root will not get a name |
array | bool | tells the mapper if this should be an array or not | |
path_to_iterable | array[str|int] | path to itrable data where this and child parts of the configuration should be applied per iteration | |
attributes | array[attribute] | An array of this objects attribute mappings | |
objects | array[object] | Here you can nest more objects. | |
branching_objects | array[branching object] | Array of a special kind of object | rarely used |
{
"name": "object_name",
"array": true,
"path_to_iterable": ["path", "to", "list"],
"objects": [],
"branching_objects": [],
"attributes": []
}
Attribute¶
The attributes are like 'color' of a car or 'amount' in an invoice. Attributes are have a name ('amount'), a number of mappings, separator, if statements, casting and a default value if all else fails.
name | type | description | default |
---|---|---|---|
name | str | The name it will get in the parent object | |
mappings | array[mapping] | list of mapping objects which is where to find data | [] |
separator | str | string to separate each value in case multiple are found in mapping step | '' |
if_statements | array[if statement] | If statements that can change data based on conditions | [] |
casting | casting | Lets you cast data to a spesific type [int, decimal, date] | {} |
default | Any | If after all mapping, if statements and casting the result is None this value is used | None |
{
"name": "attribute_name",
"mappings": [],
"separator": "",
"if_statements": [],
"casting": {},
"default": "default value"
}
Mapping¶
This is the only place where actual interaction with the input data is done.
name | type | description | default |
---|---|---|---|
path | array[str|int] | path to data you want to retrieve. | [] |
if_statements | array[if statement] | If statements that can change data based on conditions | [] |
default | Any | If no value is found or value is None after if_statements then this value is used | None |
Note
either path
or default
must contain a something
Explanation of path
You add a list of strings
or integers
that will get you to your data. so for example if you needed to get to the second element in the list called my_list
in the following json then your path
will be ["my_list", 1]
and you will get the value index1
{
"my_list": ["index0", "index1"]
}
- if_statements: list of if statements that can change the data depending on conditions
- default: a default value if none is found or value found is
None
{
"path": ["path", "to", "data"],
"if_statements": [],
"default": "default"
}
input({'path': { 'to': { 'data': 'value'}}}) -> 'value'
input({'path': { 'does_not_exist'}}) -> 'default'
input() -> 'default'
Slicing¶
Lets you slice a value from index from
to index to
. Slicing is implemented exactly like pythons string[x:x] slicing. This means that when from
is negative you count back from the end, and if to
is null
or left out then we consume the rest of the string.
name | type | description | default |
---|---|---|---|
from | int | Where to cut value from counted from 0 |
|
to | int|null | To what index we cut to, leave key/value out or set value=null to go to end of string |
{
"from": 1,
"to": 3,
}
input('hello') -> 'el'
Note
All values are turned into string
before slicing is applied. This lets you also slice any values independant on their original type in the input data. If the config Slicing object is empty, this str casting is also skipped. Any result after slicing is also a string. So if you need a different format use casting to change it
If Statement¶
This is where you can change found(or not found) data to something else based on a condition. They are chained in the sense that what the first one produces will be the input to the next one. Thus if you want the original value if the first one fails, then leave out otherwise
name | type | description | default |
---|---|---|---|
condition | one of ["is", "not", "in", "contains"] | What condition to use when checking value against target |
|
target | str|number|bool|array | Target what we do our condition against ie: value == target when condition is is |
|
then | str|number|bool | value that we will return if the condition is true | |
otherwise | str|number|bool | Optional value that we can return if the condition is false | None |
{
"condition": "is",
"target": "1",
"then": "first_type",
"otherwise": "default_type"
}
input('2') -> 'default_type'
input('1') -> 'first_type'
Casting¶
The casting object lets you cast whatever value is found to some new value. Currently integer, decimal and date are supported and original format is optional helper data that we need for some special cases where the format of the input value cannot be asserted automatically.
name | type | description | default |
---|---|---|---|
to | one of ["integer", "decimal", "date"] | What type to cast the value to | |
original_format | "integer_containing_decimals" or spesific date format(see below)" | For some values we need to specify extra information in order to correctly cast it. | None |
about original format
Note
When to
is date
then original_format is required
.
when to is | original format | description |
---|---|---|
decimal | integer_containing_decimals | is used when some integer value should be casted to decimal and we need to divide it by 100 |
date | yyyy.mm.dd yy.mm.dd yymmdd dd.mm.yyyy dd.mm.yy ddmmyy |
The format of the input date. . means any delimiter. Output is always iso-date yyyy-mm-dd |
Examples
{
"to": "decimal",
"original_format": "integer_containing_decimals"
}
"10050" -> Decimal(100.50)
{
"to": "date",
"original_format": "ddmmyyyy"
}
"01012001" -> "2010-01-01"
Branching Object¶
The branching object is a special object that does not have attributes or object childs but has a special branching_attributes child. The point of this object is to make sure that we can map data from different sources into the same element. for example, we have an object called "extradata" with the attributes 'name' and 'data'. This is kind of a field that can be many things. like 'name' = 'extra_address_line1', and another one with 'extra_address_line2'. This must then get its data from different places, and thats what these branching objects are for.
name | type | description | default |
---|---|---|---|
name | str | Name of the object | |
array | bool | if it should be an array or not | |
path_to_iterable | array[str, int] | path to list | [] |
branching_attributes | array[array[attribute]] | list of list of attributes where each list of attributes will create a branching object. |
Example
{
"name": "extradata",
"array": true,
"branching_attributes": [
[
{
"name": "name",
"default": "extra_address_line1"
},
{
"name": "data",
"mappings": [{"path": ["list", "to", "line1", "value"]}]
}
],
[
{
"name": "name",
"default": "extra_address_line2"
},
{
"name": "data",
"mappings": [{"path": ["list", "to", "line2", "value"]}]
}
]
]
}
This will produce:
{
"extradata": [
{
"name": "extra_address_line1",
"data": "address value 1"
},
{
"name": "extra_address_line2",
"data": "address value 2"
}
]
}