Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
10f567e
Update .gitignore
GorkiPower Jan 20, 2026
29ab7ea
Update pyproject.toml src/ tests/
GorkiPower Jan 22, 2026
35abba2
schema_flattening.py
GorkiPower Jan 27, 2026
27eb1aa
Lost in the Forest - Reset ~ Starting with JUST one python-script
GorkiPower Jan 27, 2026
17230f4
Reset - Start with Skeleton
GorkiPower Jan 27, 2026
429133f
Working Code
GorkiPower Jan 27, 2026
da58a8d
Update - Cleaning up the Code
GorkiPower Jan 27, 2026
ff735eb
Cleaning the Code
GorkiPower Jan 27, 2026
51611f9
Update jflat.py
GorkiPower Jan 28, 2026
834087f
Cleaning up Code
GorkiPower Jan 28, 2026
9d4e48d
Update jflat from review notes
GorkiPower Jan 29, 2026
746b1d4
Simplify; jflat.py implement flatten_sjonn()
GorkiPower Jan 29, 2026
329b94d
Refactor struture: move models to examples, simplified -> now flatten…
GorkiPower Jan 29, 2026
8d3a77f
Update pyproject.toml
GorkiPower Jan 29, 2026
2a7fba0
pyproject.toml is created after BluePrint from Bam-Master
GorkiPower Jan 29, 2026
edf1c6f
Update README.md with clearer project information
GorkiPower Jan 29, 2026
b72db6e
Comments are Updated
GorkiPower Jan 30, 2026
d7ce084
indent from 4 to 2 -> indent=2
GorkiPower Jan 30, 2026
bbb2e06
added ToDo in schema.py - NOT implemented jet
GorkiPower Jan 30, 2026
8c3a1b2
Starting with update of pyproject.toml
GorkiPower Jan 30, 2026
c1eeb9b
pyproject.toml configuration/setup src
GorkiPower Jan 30, 2026
eca8c55
in .gitignore add: src/jflat/_version.py ; so the _version.py is NOT …
GorkiPower Jan 30, 2026
78d37bb
implementing: flatten_json_schema
GorkiPower Jan 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# added
.MyNotes/
.MyNotes/*


# Generated by setuptools-scm (do not commit)
src/jflat/_version.py


# Byte-compiled / optimized / DLL files
__pycache__/
*.py[codz]
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,27 @@
***
# jflat
Utility functions to transform nested JSON schemas into a flat list of dictionaries.

***
# jflat

A small Python utility to transform **nested JSON objects** into a **flat dictionary** with underscore‐separated keys.

This project demonstrates:
- A clean `src/`-based Python package layout
- A minimal Pydantic schema example
- Conversion from nested Pydantic model output to a flat JSON using `flatten_json`
- Example schema JSON generation (`examples/schema_json.json`)
- Pytest-based unit tests

---

## 📦 Installation (development mode)

Clone the repository and install in editable mode:

```bash
pip install -e .
```

***
40 changes: 40 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[build-system]
requires = ["setuptools>=61.0.0", "wheel", "setuptools-scm"]
build-backend = "setuptools.build_meta"

[project]
name = "jflat"
dynamic = ["version"]
description = "Utility functions to flatten nested JSON into a flat dictionary."
readme = "README.md"
authors = [
{ name = "Maxim Köppel" }
]
requires-python = ">=3.10"

dependencies = [
"pydantic>=2.0"
]

classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
]

[project.urls]
repository = "https://github.com/BAMResearch/jflat"
homepage = "https://github.com/BAMResearch/jflat"

[tool.setuptools]
package-dir = { "" = "src" }

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools_scm]
write_to = "src/jflat/_version.py"

[tool.pytest.ini_options]
pythonpath = [
"src"
]
15 changes: 15 additions & 0 deletions src/examples/export_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from schema import Method
import json

"""
File to create a JSON Schema (stored in `./examples/schema_json.json`) based on the example data model defined
in `./examples/schema.py`.

This JSON Schema is produced by calling `Method.model_json_schema()`, where `Method` is defined in `schema.py`.
"""

schema = Method.model_json_schema()

with open("schema_json.json", "w") as f:
json.dump(schema, f, indent=2)

68 changes: 68 additions & 0 deletions src/examples/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from pydantic import BaseModel, Field

class Example(BaseModel):
id: str = Field(..., description="The unique identifier")


class Person(BaseModel):
name: str = Field(..., description="The person's name")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you can define more types in this example? Something like str | none, and create more different Fields (so the printed JSON schema is actually richer and we can cover more cases)

age: int = Field(..., ge=0, description="The person's age in years")
example: Example


class BaseMethod(BaseModel):
author: str = Field(..., description="The author of the method")


class Method(BaseMethod):
method_name: str = Field(..., description="The name of the method")
person: Person


#ToDo Maybe you can define more types in this example? Something like str | none, and create more different Fields (so the printed JSON schema is actually richer and we can cover more cases)


"""
src/jflat/jflat.py def flatten_json(data: Dict[str, Any], parent_key: str = "") -> Dict[str, Any]:

The goal of this function is more to map a JSON Schema (as printed by pydantic) to our new flattened version. The end result should look something like:

{
"$defs": {
"Method": {
"properties": {
"author": {
"description": "The author of the method",
"type": "string"
},
"method_name": {
"description": "The name of the method",
"type": "string"
},
"person": {
"$ref": "#/$defs/Person"
}
},
"description": "...<whatever-here>...",
"$inherits_from": "#/$defs/BaseMethod",
},
<other classes here>
}}
As you can see, I slightly modified the resulting JSON schema when printed using model_json_schema. The idea is to get rid off unnecessary stuff and adding some other info. I:

Deleted title in each property
Deleted title in each object
Added an $inherits_from key in each of the objects dictionaries
Moved the Method defs inside $defs (before, it is outside because we are printing from it)
We need to add BaseMethod to define inheritances
Deleted all the required and type:object stuff
We could also:

Add a key inside each property defining if they are mandatory or optional (this was before defined by required. Somethind like:
"author": {
"description": "The author of the method",
"type": "string",
"mandatory": true # this can also be false, if the property is optional (i.e., str | None)
},

"""
65 changes: 65 additions & 0 deletions src/examples/schema_json.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"$defs": {
"Example": {
"properties": {
"id": {
"description": "The unique identifier",
"title": "Id",
"type": "string"
}
},
"required": [
"id"
],
"title": "Example",
"type": "object"
},
"Person": {
"properties": {
"name": {
"description": "The person's name",
"title": "Name",
"type": "string"
},
"age": {
"description": "The person's age in years",
"minimum": 0,
"title": "Age",
"type": "integer"
},
"example": {
"$ref": "#/$defs/Example"
}
},
"required": [
"name",
"age",
"example"
],
"title": "Person",
"type": "object"
}
},
"properties": {
"author": {
"description": "The author of the method",
"title": "Author",
"type": "string"
},
"method_name": {
"description": "The name of the method",
"title": "Method Name",
"type": "string"
},
"person": {
"$ref": "#/$defs/Person"
}
},
"required": [
"author",
"method_name",
"person"
],
"title": "Method",
"type": "object"
}
76 changes: 76 additions & 0 deletions src/examples/schema_json_aiGenerated.json
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need an AI generated json?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, DISCUSSION:
I was wondering, if the flatten Output is good.
Copilote generated a json schema, maybe there is room for improvement?

Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

{
"title": "Method",
"type": "object",
"properties": {
"author": {
"title": "Author",
"description": "The author of the method",
"type": "string"
},
"method_name": {
"title": "Method Name",
"description": "The name of the method",
"type": "string"
},
"person": {
"title": "Person",
"allOf": [
{
"$ref": "#/$defs/Person"
}
]
}
},
"required": [
"author",
"method_name",
"person"
],
"$defs": {
"Example": {
"title": "Example",
"type": "object",
"properties": {
"id": {
"title": "Id",
"description": "The unique identifier",
"type": "string"
}
},
"required": [
"id"
]
},
"Person": {
"title": "Person",
"type": "object",
"properties": {
"name": {
"title": "Name",
"description": "The person's name",
"type": "string"
},
"age": {
"title": "Age",
"description": "The person's age in years",
"type": "integer",
"minimum": 0
},
"example": {
"title": "Example",
"allOf": [
{
"$ref": "#/$defs/Example"
}
]
}
},
"required": [
"name",
"age",
"example"
]
}
}
}
4 changes: 4 additions & 0 deletions src/jflat/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

from .jflat import flatten_json

__all__ = ["flatten_json"]
31 changes: 31 additions & 0 deletions src/jflat/jflat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import Any, Dict


def flatten_json(data: Dict[str, Any], parent_key: str = "") -> Dict[str, Any]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal of this function is more to map a JSON Schema (as printed by pydantic) to our new flattened version. The end result should look something like:

{
    "$defs": {       
        "Method": {
            "properties": {
                "author": {
                   "description": "The author of the method",
                   "type": "string"
                },
                "method_name": {
                   "description": "The name of the method",
                   "type": "string"
                },
                "person": {
                   "$ref": "#/$defs/Person"
                }
            },
            "description": "...<whatever-here>...",
            "$inherits_from": "#/$defs/BaseMethod",
        },       
        <other classes here>
}}

As you can see, I slightly modified the resulting JSON schema when printed using model_json_schema. The idea is to get rid off unnecessary stuff and adding some other info. I:

  • Deleted title in each property
  • Deleted title in each object
  • Added an $inherits_from key in each of the objects dictionaries
  • Moved the Method defs inside $defs (before, it is outside because we are printing from it)
  • We need to add BaseMethod to define inheritances
  • Deleted all the required and type:object stuff

We could also:

  • Add a key inside each property defining if they are mandatory or optional (this was before defined by required. Somethind like:
        "author": {
            "description": "The author of the method",
            "type": "string",
            "mandatory": true   # this can also be false, if the property is optional (i.e., str | None)
        },

"""
Flatten a nested JSON-like dictionary into a flat dictionary
using underscore-separated keys.

Example:
{"person": {"name": "Alice"}}
becomes:
{"person_name": "Alice"}
"""
result: Dict[str, Any] = {}

for key, value in data.items():
new_key = f"{parent_key}_{key}" if parent_key else key

if isinstance(value, dict):
# Recursively flatten child dictionaries
result.update(flatten_json(value, new_key))
else:
result[new_key] = value

return result




def flatten_json_schema():
pass
25 changes: 25 additions & 0 deletions tests/test_jflat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

from jflat.jflat import flatten_json


def test_flatten_json_simple():
data = {"a": 1, "b": 2}
assert flatten_json(data) == {"a": 1, "b": 2}


def test_flatten_json_nested():
data = {
"person": {
"name": "Alice",
"info": {
"age": 30
}
}
}

flattened = flatten_json(data)

assert flattened == {
"person_name": "Alice",
"person_info_age": 30
}