Skip to main content
Yanked

This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 0.8.2 instead.
Reason given by maintainers: premature bot release

json-merge-tree

JSON Merge Tree (built on top of json-multi-merge) merges JSON objects in a hierarchy, allowing customization of how objects are merged.

The hierarchy is defined by the caller's parents module, which specifies a function for each resource type that returns the parent ID and another function to get its parents (unless it's the top level).

It relies on a SQLAlchemy Table containing the fields id (UUID), resource_id (UUID), resource_type (string), slug (string), inherits (boolean), draft_set (string), created_at (timestamp), and a field that stores the JSON object you want to merge.

Installation

Install this package, e.g., pip install json-merge-tree.


## merge_tree

Main entrypoint for merging JSON objects in a table up a hierarchy defined by `parents`.

### Example
```python
from json_merge_tree import merge_tree

import widget_service.parents
from widget_service.db import widgets

# Merge widgets of all the requested resource's ancestors and return the result
merged_json = merge_tree(
    table=widgets, 
    id='228135fb-6a3d-4551-93db-17ed1bbe466a', 
    type='brand', 
    json_field='widget', 
    parents=widget_service.parents, 
    slugs=None, 
    debug='annotate'
)

Selected columns

The query selects only resource_id, slug, inherits, and the json_field column, which the merge reads, and id, which callbacks commonly read. When debug is set, it also selects resource_type and draft_set for the debug annotations.

The records passed to callback contain only these columns. If your callback reads any other column, add it to the query with buff_query:

def buff_query(C, query, filters):
    return query.add_columns(C.updated_at), filters

def callback(record):
    print(record.updated_at)

merged_json = merge_tree(
    table=widgets,
    id='228135fb-6a3d-4551-93db-17ed1bbe466a',
    type='brand',
    json_field='widget',
    parents=widget_service.parents,
    buff_query=buff_query,
    callback=callback,
)

async_merge_tree

The asyncio version of merge_tree, taking the same arguments and returning the same result.

  • db is an AsyncConnection, or an AsyncEngine. Given an engine, it connects only for the one query, after all the parents have been fetched, so no connection is held while the parent getters call other services.
  • The getters in parents may be coroutine functions that return the (id, type, getter) tuples, or plain functions/generators like those merge_tree takes.
  • Each resource's parents are fetched concurrently (e.g. a page's site and section chains), but merged in the order their getter returned them, so the result is the same as merge_tree's.
from json_merge_tree import async_merge_tree

merged_json = await async_merge_tree(
    db=read_engine,
    table=widgets,
    id='228135fb-6a3d-4551-93db-17ed1bbe466a',
    type='brand',
    json_field='widget',
    parents=widget_service.parents,
)

AsyncTreeMerger is the class behind it, as TreeMerger is behind merge_tree.

merge

Generic function to merge two JSON objects together. Largely the same as jsonmerge.Merger.merge but with the added ability to customize how objects are merged with the annotations below.

Merge key annotations

You can append the annotation listed below to a key at any level to customize how its value affects the merged json.

-- Unset

Unset this key in the merged json. The value of this key does not matter - you can set it to null.

  • E.g. 1
{
  "colors": {
    "content": "#000000", 
    "section--": null
  }
}

merged into

{
  "colors": {
    "section": "#000001"
  }
}

results in

{
  "colors": {
    "content": "#000000"
  }
}

  • E.g. 2
{
  "styles": {
    "h1--": null, 
    "h2": {
      "fontSize": 3
    }
  }
}

merged into

{
  "styles": {
    "h1": {
      "fontWeight": "heading"
    }
  }
}

results in

{
  "styles": {
    "h2": {
      "fontSize": 3
    }
  }
}

! Replace

Replace this key's value with this value.

  • E.g. 1
{
  "colors": {
      "content": "#000000", 
      "section!": "#000002"
  }
}

merged into

{
  "colors": {
    "section": "#000001"
  }
}

results in

{
  "colors": {
    "content": "#000000", 
    "section": "#000002"
  }
}

  • E.g. 2
{
  "styles": {
    "h1!": {
      "fontFamily": "heading",
      "fontSize": 5
    }
  }
}

merged into

{
  "styles": {
    "h1": {
      "fontWeight": "heading"
    }
  }
}

results in

{
  "styles": {
    "h1": {
      "fontFamily": "heading",
      "fontSize": 5
    }
  }
}

Slugs

Slugs are kind of a weird feature – and getting weirder as we use them to solve more use cases.

Originally, A slug was a named json object scoped under a resource in the hierarchy to be merged. In our case, a custom-page theme merged under one of the resources in our resource hierarchy.

The use of slugs has been extended to included named json object mixins not associated with any one resource at the bottom of the hierarchy, but scoped to a resource at any level. For example, a site wants to have a "dark" theme that could be applied to any page within the site.

When merging, the library first merges the json objects without slugs, then merges json objects with slugs at each level. Multiple slugs can be included when merging.

slugs_only

When slugs_only=True is passed to merge_tree, the resource JSON at every level of the hierarchy is skipped — only slug JSON objects are merged. This is useful when you only need the slug data without any of the base resource JSON mixed in.

merged_json = merge_tree(
    table=widgets,
    id='228135fb-6a3d-4551-93db-17ed1bbe466a',
    type='brand',
    json_field='widget',
    parents=widget_service.parents,
    slugs=['dark-theme'],
    slugs_only=True,
)

Note that the underlying query still fetches all resource records (to check inherits and maintain correct cache invalidation), it just excludes them from the merge.

Metadata

Release files for json-merge-tree 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for json-merge-tree 0.9.0
File Size Uploaded Provenance Actions
json_merge_tree-0.9.0.tar.gz 6.3 kB Without provenance Details for json_merge_tree-0.9.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for json-merge-tree 0.9.0
File Interpreter ABI Platform Actions
json_merge_tree-0.9.0-py3-none-any.whl Python 3 none any Details for json_merge_tree-0.9.0-py3-none-any.whl

Total release size: 13.4 kB

Release files / json_merge_tree-0.9.0.tar.gz

Download URL json_merge_tree-0.9.0.tar.gz
Size 6.3 kB
Tags Source
SHA-256 checksum
How to use checksums
c549fcc52704405e2f85a9e15069ff2769655e3a588ca534824b80db6c30b888
BLAKE2b-256 checksum
How to use checksums
08ece54096d8efbde4be73bf6c59ad281891bca4ef0e395fb282dc2aa4f82575
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.22

Release files / json_merge_tree-0.9.0-py3-none-any.whl

Download URL json_merge_tree-0.9.0-py3-none-any.whl
Size 7.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0c5f1dc3a7625bc4356212949482cb96fe1c635ab93f2e964146fa1b595bc02b
BLAKE2b-256 checksum
How to use checksums
23e3a103e2be12f55ff520ccdfcd835e45485f4f0efaf80b7bb4c8c44b501bc3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.22

Security for json-merge-tree 0.9.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page