]> ToastFreeware Gitweb - philipp/winterrodeln/wrpylib.git/blobdiff - wrpylib/json_tools.py
Omit empty parameters in key/value templates.
[philipp/winterrodeln/wrpylib.git] / wrpylib / json_tools.py
index 8490b5947e9569f74f20e4292e4398b4b37c2450..f4aa192887596d545bc2ae80ee7d8bd9c82fc81a 100644 (file)
@@ -12,19 +12,63 @@ def _fmt_path(path: List) -> str:
     return f'schema[{"][".join(map(repr, path))}]'
 
 
-def _order_json_keys_string(sub_value: JsonTypes, sub_schema: JsonTypes, path: List) -> str:
+def _resolve_ref_not_recursive(sub_schema: JsonTypes, schema: JsonTypes) -> JsonTypes:
+    """In case the sub_schema is a dict and has direct "$ref" keys,
+    it is replaced by a dict where the "$ref" key is replaced by the corresponding definition in schema.
+    Nested $ref keys are not resolved.
+    Recursive $ref keys are not resolved.
+
+    :param sub_schema: JSON sub-schema where a "$ref" key is possible replaced.
+        The value of "$ref" could be e.g. "#/definitions/position"
+    :param schema: JSON root schema containing definitions for the keys.
+    :raise ValidationError: In case a "$ref" could not be resolved.
+    """
+    if not isinstance(sub_schema, dict) or '$ref' not in sub_schema:
+        return sub_schema
+    ref = sub_schema['$ref']
+    if not isinstance(ref, str):
+        raise ValidationError(f'Type of reference {ref} is not string.')
+    path = ref.split('/')
+    if len(path) == 0 or path[0] != '#':
+        raise ValidationError(f'Unsupported reference {ref}.')
+    ref_schema = schema
+    for p in path[1:]:
+        if not isinstance(ref_schema, dict) or p not in ref_schema:
+            raise ValidationError(f'Reference path {ref} not found in schema.')
+        ref_schema = ref_schema[p]
+    if not isinstance(ref_schema, dict):
+        raise ValidationError(f'Reference path {ref} is no dict.')
+    sub_schema = sub_schema.copy()
+    del sub_schema['$ref']
+    resolved_schema = ref_schema.copy()
+    resolved_schema.update(sub_schema)
+    return resolved_schema
+
+
+def _resolve_ref(sub_schema: JsonTypes, schema: JsonTypes) -> JsonTypes:
+    """Same as `_resolve_ref_not_recursive` but recursively resolves $ref keys.
+    However, does not resolve nested $ref keys.
+    """
+    resolved_schema = sub_schema
+    while isinstance(resolved_schema, dict) and '$ref' in resolved_schema:
+        resolved_schema = _resolve_ref_not_recursive(resolved_schema, schema)
+    return resolved_schema
+
+
+def _order_json_keys_string(sub_value: JsonTypes, sub_schema: JsonTypes, schema: JsonTypes, path: List) -> str:
     if not isinstance(sub_value, str):
         raise ValidationError(f'Type of {_fmt_path(path)} needs to be string (Python str).')
     return sub_value
 
 
-def _order_json_keys_number(sub_value: JsonTypes, sub_schema: JsonTypes, path: List) -> Union[int, float]:
+def _order_json_keys_number(sub_value: JsonTypes, sub_schema: JsonTypes,
+                            schema: JsonTypes, path: List) -> Union[int, float]:
     if not isinstance(sub_value, (int, float)) or isinstance(sub_value, bool):
         raise ValidationError(f'Type of {_fmt_path(path)} needs to be number (Python int or float).')
     return sub_value
 
 
-def _order_json_keys_object(sub_value: JsonTypes, sub_schema: JsonTypes, path: List) -> Dict:
+def _order_json_keys_object(sub_value: JsonTypes, sub_schema: JsonTypes, schema: JsonTypes, path: List) -> Dict:
     if not isinstance(sub_value, dict):
         raise ValidationError(f'Type of {_fmt_path(path)} needs to be object (Python dict).')
     v = sub_value.copy()
@@ -32,7 +76,7 @@ def _order_json_keys_object(sub_value: JsonTypes, sub_schema: JsonTypes, path: L
     result = {}
     for key in p:
         if key in v:
-            result[key] = _order_json_keys(v.pop(key), p[key], path + [key])
+            result[key] = _order_json_keys(v.pop(key), p[key], schema, path + [key])
         else:
             if key in sub_schema.get('required', []):
                 raise ValidationError(f'Required key "{key}" not present ({_fmt_path(path)}).')
@@ -45,39 +89,42 @@ def _order_json_keys_object(sub_value: JsonTypes, sub_schema: JsonTypes, path: L
     return result
 
 
-def _order_json_keys_array(sub_value: JsonTypes, sub_schema: JsonTypes, path: List) -> List:
+def _order_json_keys_array(sub_value: JsonTypes, sub_schema: JsonTypes, schema: JsonTypes, path: List) -> List:
     if not isinstance(sub_value, list):
         raise ValidationError(f'Type of {"".join(_fmt_path(path))} needs to be array (Python list).')
     s = sub_schema.get('items', True)
-    return [_order_json_keys(v, s, path + [i]) for i, v in enumerate(sub_value)]
+    return [_order_json_keys(v, s, schema, path + [i]) for i, v in enumerate(sub_value)]
 
 
-def _order_json_keys_boolean(sub_value: JsonTypes, sub_schema: JsonTypes, path: List) -> bool:
+def _order_json_keys_boolean(sub_value: JsonTypes, sub_schema: JsonTypes, schema: JsonTypes, path: List) -> bool:
     if not isinstance(sub_value, bool):
         raise ValidationError(f'Type of {_fmt_path(path)} needs to be boolean (Python bool).')
     return sub_value
 
 
-def _order_json_keys_null(sub_value: JsonTypes, sub_schema: JsonTypes, path: List) -> None:
+def _order_json_keys_null(sub_value: JsonTypes, sub_schema: JsonTypes, schema: JsonTypes, path: List) -> None:
     if sub_value is not None:
         raise ValidationError(f'Type of {_fmt_path(path)} needs to be null (Python None).')
     return sub_value
 
 
-def _order_json_keys(sub_value: JsonTypes, sub_schema: JsonTypes, path: List) -> JsonTypes:
+def _order_json_keys(sub_value: JsonTypes, sub_schema: JsonTypes, schema: JsonTypes, path: List) -> JsonTypes:
     if isinstance(sub_schema, bool):
         if sub_schema:
             return sub_value
         raise ValidationError(f'Value {sub_value} not allowed in {_fmt_path(path)}.')
+    if isinstance(sub_schema, dict):
+        sub_schema = _resolve_ref(sub_schema, schema)
     return {
         'string': _order_json_keys_string,
         'number': _order_json_keys_number,
+        'integer': _order_json_keys_number,
         'object': _order_json_keys_object,
         'array': _order_json_keys_array,
         'boolean': _order_json_keys_boolean,
         'null': _order_json_keys_null,
-    }[sub_schema['type']](sub_value, sub_schema, path)
+    }[sub_schema['type']](sub_value, sub_schema, schema, path)
 
 
 def order_json_keys(value: JsonTypes, schema: JsonTypes) -> JsonTypes:
-    return _order_json_keys(value, schema, [])
+    return _order_json_keys(value, schema, schema, [])