# How to Use JSONProperty for Storing Nested JSON Data in neomodel

> Learn how to use JSONProperty in neomodel to effectively store nested JSON data in Neo4j. Serialize and deserialize Python dictionaries and lists seamlessly.

- Repository: [Neo4j Contrib/neomodel](https://github.com/neo4j-contrib/neomodel)
- Tags: how-to-guide
- Published: 2026-03-08

---

**`JSONProperty` is a neomodel property type that serializes Python data structures to JSON strings when saving and deserializes them back when loading, enabling storage of nested dictionaries and lists in Neo4j scalar properties.**

Storing complex nested data in Neo4j requires serialization since the database only supports scalar property values. The `JSONProperty` class in the `neo4j-contrib/neomodel` repository provides a seamless way to persist arbitrary Python objects—including dictionaries containing lists of dictionaries—as JSON strings while maintaining a native Python interface.

## Understanding JSONProperty Implementation

In [`neomodel/properties.py`](https://github.com/neo4j-contrib/neomodel/blob/main/neomodel/properties.py) (lines 666-684), the `JSONProperty` class inherits from the base `Property` class and participates in neomodel's inflate/deflate pipeline. This integration allows automatic conversion between Python objects and JSON strings without manual intervention during node operations.

### The Deflate Method (Serialization)

When saving a node, the `deflate` method executes `json.dumps()` on your Python object, converting dictionaries, lists, numbers, and booleans into a string format that Neo4j can store. According to the source code, you can configure the `ensure_ascii` parameter to preserve Unicode characters rather than escaping them.

### The Inflate Method (Deserialization)

Upon retrieval, the `inflate` method runs `json.loads()` on the stored string, reconstructing the original Python data structures. This process occurs transparently in [`neomodel/sync_/property_manager.py`](https://github.com/neo4j-contrib/neomodel/blob/main/neomodel/sync_/property_manager.py) during database read operations, ensuring you work with native Python types rather than raw JSON strings.

## Defining Models with JSONProperty

To store nested JSON data, define a `StructuredNode` subclass and assign `JSONProperty` to any attribute requiring complex data structures. The property accepts an optional `ensure_ascii` argument to control Unicode handling.

```python
from neomodel import StructuredNode, StringProperty, JSONProperty

class Person(StructuredNode):
    name = StringProperty(required=True)
    # Store any nested JSON-compatible data

    profile = JSONProperty(ensure_ascii=False)  # Preserve Unicode characters

```

## Creating and Retrieving Nested Data

When you assign nested dictionaries or lists to a `JSONProperty` attribute and call `save()`, neomodel automatically serializes the data through the deflate pipeline implemented in [`neomodel/sync_/node.py`](https://github.com/neo4j-contrib/neomodel/blob/main/neomodel/sync_/node.py). Upon retrieval via `Person.nodes.get()`, the inflate process reconstructs the Python objects.

```python

# Create a node with deeply nested structure

jane = Person(name="Jane Doe").save()
jane.profile = {
    "age": 34,
    "address": {
        "street": "123 Main St",
        "city": "München",
        "coordinates": {"lat": 48.1351, "lon": 11.5820}
    },
    "preferences": ["coffee", "cycling", {"sport": "skiing", "level": "intermediate"}]
}
jane.save()  # Serializes to JSON string via deflate()

# Retrieve - JSON automatically inflated to Python dict

same_jane = Person.nodes.get(name="Jane Doe")
print(same_jane.profile["address"]["city"])  # Output: München

```

## Updating Partial JSON Structures

Since `JSONProperty` returns mutable Python objects, you can modify nested values and save the node. However, you must reassign the property to trigger the deflate cycle in [`neomodel/sync_/property_manager.py`](https://github.com/neo4j-contrib/neomodel/blob/main/neomodel/sync_/property_manager.py).

```python
profile = same_jane.profile
profile["preferences"].append("reading")
same_jane.profile = profile  # Required to trigger serialization

same_jane.save()

```

## Querying JSONProperty Content

Neo4j stores `JSONProperty` values as strings, limiting query capabilities to string operations. You cannot query specific nested keys directly; instead, use string containment filters to match substrings within the JSON representation.

```python

# Find profiles containing the word "cycling" (string match)

cycling_people = Person.nodes.filter(profile__contains='"cycling"')

```

## Summary

- `JSONProperty` in [`neomodel/properties.py`](https://github.com/neo4j-contrib/neomodel/blob/main/neomodel/properties.py) serializes Python objects to JSON strings using `json.dumps()` during the deflate phase.
- The `inflate` method in the same file deserializes stored strings back to Python objects using `json.loads()`.
- Set `ensure_ascii=False` to preserve Unicode characters in your JSON data.
- Since Neo4j stores these as scalar strings, querying is limited to string matching operations rather than structured JSON queries.
- Always reassign the property value after mutation to ensure neomodel detects changes and triggers serialization.

## Frequently Asked Questions

### What is the difference between JSONProperty and ArrayProperty in neomodel?

`ArrayProperty` stores homogeneous lists of primitive Neo4j types (strings, integers, booleans) as native Neo4j arrays, allowing indexed access and type-specific queries. `JSONProperty` stores heterogeneous nested structures as serialized JSON strings, supporting dictionaries containing mixed types and nested objects, but with limited queryability since Neo4j treats the value as a single string.

### Can I use JSONProperty with the async API in neomodel?

Yes, `JSONProperty` works with both synchronous and asynchronous neomodel APIs. While the examples shown use the synchronous implementation in [`neomodel/sync_/node.py`](https://github.com/neo4j-contrib/neomodel/blob/main/neomodel/sync_/node.py), the property class itself is agnostic to the sync/async boundary. The inflate/deflate logic executes during property management regardless of which API layer handles the database connection.

### How do I handle Unicode characters when using JSONProperty?

Pass `ensure_ascii=False` when defining the property: `profile = JSONProperty(ensure_ascii=False)`. This parameter passes directly to Python's `json.dumps()` method in the `deflate` implementation, preventing Unicode characters from being escaped as ASCII sequences and preserving human-readable international text in your Neo4j database.

### Is it possible to index or query specific keys inside a JSONProperty?

No. Because `JSONProperty` stores data as serialized strings in Neo4j, the database cannot index individual nested keys or values. You can only perform string containment queries using filters like `profile__contains`. For queryable nested structures, consider modeling the data as separate nodes and relationships rather than using `JSONProperty`.