jmesflat


Namejmesflat JSON
Version 0.0.2 PyPI version JSON
download
home_pageNone
SummaryInitialize jmesflat package
upload_time2024-08-26 02:16:52
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseNone
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # jmesflat

Built upon and considered an extension of [`jmespath`](https://jmespath.org), `jmesflat` is similarly pronounced (say "James flat") and provides a simple interface for flattening, 'unflattening', and merging deeply nested json objects.

Common use cases:

```python
>>> # 1. Building deeply nested objects without constructing individual layers:
>>> import jmesflat as jf
>>> nest1 = jf.unflatten({"a.b[0].c[0].d": "e", "a.b[1].f": "g"})
>>> nest1
{'a': {'b': [{'c': [{'d': 'e'}]}, {'f': 'g'}]}}
>>>
>>> # 2. Merging deeply nested objects:
>>> nest2 = {"a": {"b": [{"f": "g"}, {"c": [{"d": "e"}]}]}}
>>> merged_nest = jf.merge(nest1, nest2)
>>> merged_nest
{'a': {'b': [{'c': [{'d': 'e'}], 'f': 'g'}, {'c': [{'d': 'e'}], 'f': 'g'}]}}
>>>
>>> # 3. Making dumps of complex nest objects compact and human readable
>>> import json
>>> print(json.dumps(jf.flatten(merged_nest), indent=2))
{
  "a.b[0].c[0].d": "e",
  "a.b[0].f": "g",
  "a.b[1].c[0].d": "e",
  "a.b[1].f": "g"
}
```

## Installation

`pip install jmesflat`

## Compatibility

Python 3.9 or greater

## Basic Features Demo

1. Keys *can* contain spaces and reserved characters (`@` and `-`)
2. Supports any arbitrary nesting pattern including mixed and multi-level array objects
3. Empty lists / dicts are considered atomic types and included in the flattened output alongside the 'true' atomic types `int`, `float`, `str`, `bool`, and `None`
4. Flatten / unflatten / merge at an arbitrary *object* depth using the `level` parameter (depth *cannot exceed* the depth of the first array instance)
5. Extend rather than overwrite arrays during merge operations using the `array_merge` parameter. 'topdown' merges extend at the first array instance. 'bottomup' extends at the final array instance.
6. Scrub the data during `flatten`/`unflatten`/`merge` operations or simply scrub a nested object via `clean` using the `discard_check` parameter. The check is ONLY applied to `nest2` during the `merge` operation.

```python
>>> import json
>>> import jmesflat as jf
>>>
>>> test_nest = {
...     "Outer Object Key 1": {
...         "mixedArray": [
...             "mixed array string",
...             {"mixed Array Object 1 Key": "spaces demo"},
...             12345,
...             [
...                 {"@subArray": "@ symbol demo"},
...                 {"@subArray": 1.2345},
...                 {"@subArray": None},
...             ],
...             {"mixed-array-object-2-key": "dashed key demo"},
...             [],
...             {},
...         ],
...     },
...     "Outer Object Key 2": {
...         "deepNest": {
...             "a": [
...                 {"b": 1},
...                 {
...                     "c": {
...                         "d": [
...                             {"e": "f", "g": "h"},
...                             {"e": "f1"}
...                         ]
...                     }
...                 }
...             ]
...         },
...     },
... }
>>>
>>> flat = jf.flatten(test_nest, level=1)
>>> print(json.dumps(flat, indent=2))
{
  "Outer Object Key 1": {
    "mixedArray[0]": "mixed array string",
    "mixedArray[1].mixed Array Object 1 Key": "spaces demo",
    "mixedArray[2]": 12345,
    "mixedArray[3][0].@subArray": "@ symbol demo",
    "mixedArray[3][1].@subArray": "@ symbol demo",
    "mixedArray[3][2].@subArray": "@ symbol demo",
    "mixedArray[4].mixed-array-object-2-key": "dashed key demo",
    "mixedArray[5]": [],
    "mixedArray[6]": {}
  },
  "Outer Object Key 2": {
    "deepNest.a[0].b": 1,
    "deepNest.a[1].c.d[0].e": "f",
    "deepNest.a[1].c.d[0].g": "h",
    "deepNest.a[1].c.d[1].e": "f1"
  }
}
>>>
>>> jf.unflatten(flat, level=1) == test_nest
True
>>>
>>> from copy import deepcopy
>>> test_nest2 = deepcopy(test_nest)
>>> # NOTE: `jf.flatten` wrapper is used for ease of visualization only in the merge/clean examples below
>>> print(json.dumps(jf.flatten(jf.merge(test_nest, test_nest2, level=1), level=2), indent=2))
{
  "Outer Object Key 1": {
    "mixedArray": {
      "[0]": "mixed array string",
      "[1].mixed Array Object 1 Key": "spaces demo",
      "[2]": 12345,
      "[3][0].@subArray": "@ symbol demo",
      "[3][1].@subArray": 1.2345,
      "[3][2].@subArray": null,
      "[4].mixed-array-object-2-key": "dashed key demo",
      "[5]": [],
      "[6]": {}
    }
  },
  "Outer Object Key 2": {
    "deepNest": {
      "a[0].b": 1,
      "a[1].c.d[0].e": "f",
      "a[1].c.d[0].g": "h",
      "a[1].c.d[1].e": "f1"
    }
  }
}
>>> print(json.dumps(jf.flatten(jf.merge(test_nest, test_nest2, level=1, array_merge="topdown"), level=2), indent=2))
{
  "Outer Object Key 1": {
    "mixedArray": {
      "[0]": "mixed array string",
      "[1].mixed Array Object 1 Key": "spaces demo",
      "[2]": 12345,
      "[3][0].@subArray": "@ symbol demo",
      "[3][1].@subArray": 1.2345,
      "[3][2].@subArray": null,
      "[4].mixed-array-object-2-key": "dashed key demo",
      "[5]": [],
      "[6]": {},
      "[7]": "mixed array string",
      "[8].mixed Array Object 1 Key": "spaces demo",
      "[9]": 12345,
      "[10][0].@subArray": "@ symbol demo",
      "[10][1].@subArray": 1.2345,
      "[10][2].@subArray": null,
      "[11].mixed-array-object-2-key": "dashed key demo",
      "[12]": [],
      "[13]": {}
    }
  },
  "Outer Object Key 2": {
    "deepNest": {
      "a[0].b": 1,
      "a[1].c.d[0].e": "f",
      "a[1].c.d[0].g": "h",
      "a[1].c.d[1].e": "f1",
      "a[2].b": 1,
      "a[3].c.d[0].e": "f",
      "a[3].c.d[0].g": "h",
      "a[3].c.d[1].e": "f1"
    }
  }
}
>>> print(json.dumps(jf.flatten(jf.merge(test_nest, test_nest2, level=1, array_merge="bottomup"), level=2), indent=2))
{
  "Outer Object Key 1": {
    "mixedArray": {
      "[0]": "mixed array string",
      "[1].mixed Array Object 1 Key": "spaces demo",
      "[2]": 12345,
      "[3][0].@subArray": "@ symbol demo",
      "[3][1].@subArray": 1.2345,
      "[3][2].@subArray": null,
      "[3][3].@subArray": "@ symbol demo",
      "[3][4].@subArray": 1.2345,
      "[3][5].@subArray": null,
      "[4].mixed-array-object-2-key": "dashed key demo",
      "[5]": [],
      "[6]": {},
      "[7]": "mixed array string",
      "[8].mixed Array Object 1 Key": "spaces demo",
      "[9]": 12345,
      "[10].mixed-array-object-2-key": "dashed key demo",
      "[11]": [],
      "[12]": {}
    }
  },
  "Outer Object Key 2": {
    "deepNest": {
      "a[0].b": 1,
      "a[1].c.d[0].e": "f",
      "a[1].c.d[0].g": "h",
      "a[1].c.d[1].e": "f1",
      "a[1].c.d[2].e": "f",
      "a[1].c.d[2].g": "h",
      "a[1].c.d[3].e": "f1",
      "a[2].b": 1
    }
  }
}
>>> print(json.dumps(jf.flatten(jf.clean(test_nest, discard_check=lambda key, val: "-" in key or not isinstance(val, (str, int))), level=2), indent=2))
{
  "Outer Object Key 1": {
    "mixedArray": {
      "[0]": "mixed array string",
      "[1].mixed Array Object 1 Key": "spaces demo",
      "[2]": 12345,
      "[3][0].@subArray": "@ symbol demo"
    }
  },
  "Outer Object Key 2": {
    "deepNest": {
      "a[0].b": 1,
      "a[1].c.d[0].e": "f",
      "a[1].c.d[0].g": "h",
      "a[1].c.d[1].e": "f1"
    }
  }
}
```

## The `constants` Module

The `constants` module allows global defaults to be set for several key features. For example, a global discard check function can be defined by setting the value of `jf.constants.DISCARD_CHECK`. If the user wishes to discard all `None` type values, simply set `jf.constants.DISCARD_CHECK = lambda _, val: val is None` after the initial `import jmesflat as jf` statement. In addition, users can customize the default values that will be used when extending arrays during an index preserving unflatten operation via `jf.MISSING_ARRAY_ENTRY_VALUE`, a callable that accepts the flattened key of the array element *being set* and the value said *is being set to* and returns the value that should be used to pad the array until its length is >= the desired index. Other settings in the `constants` module are considered 'use at your own risk' and included for possible future extensibility.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "jmesflat",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": null,
    "author": null,
    "author_email": "Sam Hartzog <sam.hartzog@hank.ai>",
    "download_url": "https://files.pythonhosted.org/packages/40/62/48cde36b900c43f735240c10fb0b81eaf2baa62ab737c7e70cc9e5cd1364/jmesflat-0.0.2.tar.gz",
    "platform": null,
    "description": "# jmesflat\n\nBuilt upon and considered an extension of [`jmespath`](https://jmespath.org), `jmesflat` is similarly pronounced (say \"James flat\") and provides a simple interface for flattening, 'unflattening', and merging deeply nested json objects.\n\nCommon use cases:\n\n```python\n>>> # 1. Building deeply nested objects without constructing individual layers:\n>>> import jmesflat as jf\n>>> nest1 = jf.unflatten({\"a.b[0].c[0].d\": \"e\", \"a.b[1].f\": \"g\"})\n>>> nest1\n{'a': {'b': [{'c': [{'d': 'e'}]}, {'f': 'g'}]}}\n>>>\n>>> # 2. Merging deeply nested objects:\n>>> nest2 = {\"a\": {\"b\": [{\"f\": \"g\"}, {\"c\": [{\"d\": \"e\"}]}]}}\n>>> merged_nest = jf.merge(nest1, nest2)\n>>> merged_nest\n{'a': {'b': [{'c': [{'d': 'e'}], 'f': 'g'}, {'c': [{'d': 'e'}], 'f': 'g'}]}}\n>>>\n>>> # 3. Making dumps of complex nest objects compact and human readable\n>>> import json\n>>> print(json.dumps(jf.flatten(merged_nest), indent=2))\n{\n  \"a.b[0].c[0].d\": \"e\",\n  \"a.b[0].f\": \"g\",\n  \"a.b[1].c[0].d\": \"e\",\n  \"a.b[1].f\": \"g\"\n}\n```\n\n## Installation\n\n`pip install jmesflat`\n\n## Compatibility\n\nPython 3.9 or greater\n\n## Basic Features Demo\n\n1. Keys *can* contain spaces and reserved characters (`@` and `-`)\n2. Supports any arbitrary nesting pattern including mixed and multi-level array objects\n3. Empty lists / dicts are considered atomic types and included in the flattened output alongside the 'true' atomic types `int`, `float`, `str`, `bool`, and `None`\n4. Flatten / unflatten / merge at an arbitrary *object* depth using the `level` parameter (depth *cannot exceed* the depth of the first array instance)\n5. Extend rather than overwrite arrays during merge operations using the `array_merge` parameter. 'topdown' merges extend at the first array instance. 'bottomup' extends at the final array instance.\n6. Scrub the data during `flatten`/`unflatten`/`merge` operations or simply scrub a nested object via `clean` using the `discard_check` parameter. The check is ONLY applied to `nest2` during the `merge` operation.\n\n```python\n>>> import json\n>>> import jmesflat as jf\n>>>\n>>> test_nest = {\n...     \"Outer Object Key 1\": {\n...         \"mixedArray\": [\n...             \"mixed array string\",\n...             {\"mixed Array Object 1 Key\": \"spaces demo\"},\n...             12345,\n...             [\n...                 {\"@subArray\": \"@ symbol demo\"},\n...                 {\"@subArray\": 1.2345},\n...                 {\"@subArray\": None},\n...             ],\n...             {\"mixed-array-object-2-key\": \"dashed key demo\"},\n...             [],\n...             {},\n...         ],\n...     },\n...     \"Outer Object Key 2\": {\n...         \"deepNest\": {\n...             \"a\": [\n...                 {\"b\": 1},\n...                 {\n...                     \"c\": {\n...                         \"d\": [\n...                             {\"e\": \"f\", \"g\": \"h\"},\n...                             {\"e\": \"f1\"}\n...                         ]\n...                     }\n...                 }\n...             ]\n...         },\n...     },\n... }\n>>>\n>>> flat = jf.flatten(test_nest, level=1)\n>>> print(json.dumps(flat, indent=2))\n{\n  \"Outer Object Key 1\": {\n    \"mixedArray[0]\": \"mixed array string\",\n    \"mixedArray[1].mixed Array Object 1 Key\": \"spaces demo\",\n    \"mixedArray[2]\": 12345,\n    \"mixedArray[3][0].@subArray\": \"@ symbol demo\",\n    \"mixedArray[3][1].@subArray\": \"@ symbol demo\",\n    \"mixedArray[3][2].@subArray\": \"@ symbol demo\",\n    \"mixedArray[4].mixed-array-object-2-key\": \"dashed key demo\",\n    \"mixedArray[5]\": [],\n    \"mixedArray[6]\": {}\n  },\n  \"Outer Object Key 2\": {\n    \"deepNest.a[0].b\": 1,\n    \"deepNest.a[1].c.d[0].e\": \"f\",\n    \"deepNest.a[1].c.d[0].g\": \"h\",\n    \"deepNest.a[1].c.d[1].e\": \"f1\"\n  }\n}\n>>>\n>>> jf.unflatten(flat, level=1) == test_nest\nTrue\n>>>\n>>> from copy import deepcopy\n>>> test_nest2 = deepcopy(test_nest)\n>>> # NOTE: `jf.flatten` wrapper is used for ease of visualization only in the merge/clean examples below\n>>> print(json.dumps(jf.flatten(jf.merge(test_nest, test_nest2, level=1), level=2), indent=2))\n{\n  \"Outer Object Key 1\": {\n    \"mixedArray\": {\n      \"[0]\": \"mixed array string\",\n      \"[1].mixed Array Object 1 Key\": \"spaces demo\",\n      \"[2]\": 12345,\n      \"[3][0].@subArray\": \"@ symbol demo\",\n      \"[3][1].@subArray\": 1.2345,\n      \"[3][2].@subArray\": null,\n      \"[4].mixed-array-object-2-key\": \"dashed key demo\",\n      \"[5]\": [],\n      \"[6]\": {}\n    }\n  },\n  \"Outer Object Key 2\": {\n    \"deepNest\": {\n      \"a[0].b\": 1,\n      \"a[1].c.d[0].e\": \"f\",\n      \"a[1].c.d[0].g\": \"h\",\n      \"a[1].c.d[1].e\": \"f1\"\n    }\n  }\n}\n>>> print(json.dumps(jf.flatten(jf.merge(test_nest, test_nest2, level=1, array_merge=\"topdown\"), level=2), indent=2))\n{\n  \"Outer Object Key 1\": {\n    \"mixedArray\": {\n      \"[0]\": \"mixed array string\",\n      \"[1].mixed Array Object 1 Key\": \"spaces demo\",\n      \"[2]\": 12345,\n      \"[3][0].@subArray\": \"@ symbol demo\",\n      \"[3][1].@subArray\": 1.2345,\n      \"[3][2].@subArray\": null,\n      \"[4].mixed-array-object-2-key\": \"dashed key demo\",\n      \"[5]\": [],\n      \"[6]\": {},\n      \"[7]\": \"mixed array string\",\n      \"[8].mixed Array Object 1 Key\": \"spaces demo\",\n      \"[9]\": 12345,\n      \"[10][0].@subArray\": \"@ symbol demo\",\n      \"[10][1].@subArray\": 1.2345,\n      \"[10][2].@subArray\": null,\n      \"[11].mixed-array-object-2-key\": \"dashed key demo\",\n      \"[12]\": [],\n      \"[13]\": {}\n    }\n  },\n  \"Outer Object Key 2\": {\n    \"deepNest\": {\n      \"a[0].b\": 1,\n      \"a[1].c.d[0].e\": \"f\",\n      \"a[1].c.d[0].g\": \"h\",\n      \"a[1].c.d[1].e\": \"f1\",\n      \"a[2].b\": 1,\n      \"a[3].c.d[0].e\": \"f\",\n      \"a[3].c.d[0].g\": \"h\",\n      \"a[3].c.d[1].e\": \"f1\"\n    }\n  }\n}\n>>> print(json.dumps(jf.flatten(jf.merge(test_nest, test_nest2, level=1, array_merge=\"bottomup\"), level=2), indent=2))\n{\n  \"Outer Object Key 1\": {\n    \"mixedArray\": {\n      \"[0]\": \"mixed array string\",\n      \"[1].mixed Array Object 1 Key\": \"spaces demo\",\n      \"[2]\": 12345,\n      \"[3][0].@subArray\": \"@ symbol demo\",\n      \"[3][1].@subArray\": 1.2345,\n      \"[3][2].@subArray\": null,\n      \"[3][3].@subArray\": \"@ symbol demo\",\n      \"[3][4].@subArray\": 1.2345,\n      \"[3][5].@subArray\": null,\n      \"[4].mixed-array-object-2-key\": \"dashed key demo\",\n      \"[5]\": [],\n      \"[6]\": {},\n      \"[7]\": \"mixed array string\",\n      \"[8].mixed Array Object 1 Key\": \"spaces demo\",\n      \"[9]\": 12345,\n      \"[10].mixed-array-object-2-key\": \"dashed key demo\",\n      \"[11]\": [],\n      \"[12]\": {}\n    }\n  },\n  \"Outer Object Key 2\": {\n    \"deepNest\": {\n      \"a[0].b\": 1,\n      \"a[1].c.d[0].e\": \"f\",\n      \"a[1].c.d[0].g\": \"h\",\n      \"a[1].c.d[1].e\": \"f1\",\n      \"a[1].c.d[2].e\": \"f\",\n      \"a[1].c.d[2].g\": \"h\",\n      \"a[1].c.d[3].e\": \"f1\",\n      \"a[2].b\": 1\n    }\n  }\n}\n>>> print(json.dumps(jf.flatten(jf.clean(test_nest, discard_check=lambda key, val: \"-\" in key or not isinstance(val, (str, int))), level=2), indent=2))\n{\n  \"Outer Object Key 1\": {\n    \"mixedArray\": {\n      \"[0]\": \"mixed array string\",\n      \"[1].mixed Array Object 1 Key\": \"spaces demo\",\n      \"[2]\": 12345,\n      \"[3][0].@subArray\": \"@ symbol demo\"\n    }\n  },\n  \"Outer Object Key 2\": {\n    \"deepNest\": {\n      \"a[0].b\": 1,\n      \"a[1].c.d[0].e\": \"f\",\n      \"a[1].c.d[0].g\": \"h\",\n      \"a[1].c.d[1].e\": \"f1\"\n    }\n  }\n}\n```\n\n## The `constants` Module\n\nThe `constants` module allows global defaults to be set for several key features. For example, a global discard check function can be defined by setting the value of `jf.constants.DISCARD_CHECK`. If the user wishes to discard all `None` type values, simply set `jf.constants.DISCARD_CHECK = lambda _, val: val is None` after the initial `import jmesflat as jf` statement. In addition, users can customize the default values that will be used when extending arrays during an index preserving unflatten operation via `jf.MISSING_ARRAY_ENTRY_VALUE`, a callable that accepts the flattened key of the array element *being set* and the value said *is being set to* and returns the value that should be used to pad the array until its length is >= the desired index. Other settings in the `constants` module are considered 'use at your own risk' and included for possible future extensibility.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Initialize jmesflat package",
    "version": "0.0.2",
    "project_urls": {
        "Home": "https://github.com/hank-ai/jmesflat",
        "Issues": "https://github.com/hank-ai/jmesflat/issues"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "456576aed320a7b019776cb7a9b3648619fe38d7f8867f0d6ad04bca46fe88d2",
                "md5": "4fd603881fdee939db37488054add5d3",
                "sha256": "933e95ae45f75767b981421e0b62cac94b4b5c383d1a6ba9b3ff5b041b61eb4e"
            },
            "downloads": -1,
            "filename": "jmesflat-0.0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "4fd603881fdee939db37488054add5d3",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 11674,
            "upload_time": "2024-08-26T02:16:50",
            "upload_time_iso_8601": "2024-08-26T02:16:50.634661Z",
            "url": "https://files.pythonhosted.org/packages/45/65/76aed320a7b019776cb7a9b3648619fe38d7f8867f0d6ad04bca46fe88d2/jmesflat-0.0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "406248cde36b900c43f735240c10fb0b81eaf2baa62ab737c7e70cc9e5cd1364",
                "md5": "f7394d21d5fe77e5a9cc2119a656d574",
                "sha256": "10297dc5060501ff50b1b18d1b4a1e8b522b7c8b29f9cdd6c027be5f2f68fdfc"
            },
            "downloads": -1,
            "filename": "jmesflat-0.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "f7394d21d5fe77e5a9cc2119a656d574",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 14386,
            "upload_time": "2024-08-26T02:16:52",
            "upload_time_iso_8601": "2024-08-26T02:16:52.100403Z",
            "url": "https://files.pythonhosted.org/packages/40/62/48cde36b900c43f735240c10fb0b81eaf2baa62ab737c7e70cc9e5cd1364/jmesflat-0.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-26 02:16:52",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "hank-ai",
    "github_project": "jmesflat",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [],
    "lcname": "jmesflat"
}
        
Elapsed time: 0.29850s