-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Un format de données JSON colonné, compressé et optimisé pour la vitesse et le stockage.
jsonplusplus est une bibliothèque Python qui introduit le format JONX (JSON++), un format binaire optimisé conçu pour stocker et manipuler efficacement de grandes quantités de données JSON. Parfait pour l'analytique, le machine learning et les datasets volumineux.
JONX (JSON++) est un format de fichier binaire qui transforme des données JSON en un format colonné (columnar storage) avec compression Zstandard et auto-détection des types. Contrairement au JSON traditionnel qui stocke les données ligne par ligne, JONX organise les données en colonnes contiguës, permettant une compression supérieure et des accès sélectifs ultra-rapides.
| Caractéristique | JSON traditionnel | JONX (JSON++) |
|---|---|---|
| Format | Texte (UTF-8) | Binaire optimisé |
| Compression | Aucune (ou gzip) | Zstandard (niveau 7) |
| Stockage | Ligne par ligne | Colonnes contiguës |
| Types | Tous en texte | Auto-détection (int16, int32, float16, float32, bool, str, json) |
| Index | Aucun | Index triés automatiques |
| Lecture sélective | Non | Oui (décompression à la demande) |
| Performance | Lente (parsing) | Ultra-rapide (orjson + binaire) |
- Compression Zstandard : Réduction de taille jusqu'à 80% selon les données
- Stockage en colonnes : Meilleure compression pour données tabulaires
- Auto-détection des types : int16, int32, float16, float32, bool, string, json
- Index optimisés : Recherches min/max ultra-rapides sur colonnes numériques
-
Encodage/décodage rapide : Utilise
orjsonpour des performances maximales - Chargement sélectif : Décompression à la demande = moins de RAM
- Compatible Python natif : Aucune dépendance externe lourde
pip install jsonplusplusDépendances requises :
- Python >= 3.8
-
orjson>=3.9.0- Parser JSON ultra-rapide -
zstandard>=0.21.0- Compression Zstandard -
numpy>=1.20.0- Support float16
-
jonx_encode(json_path, jonx_path): Convertit un fichier JSON en fichier JONX -
encode_to_bytes(json_data): Encode des données JSON (liste d'objets) en bytes JONX
-
decode_from_bytes(byte_data): Décode des bytes JONX et retourne un dictionnaire avec les données JSON reconstruites
-
JONXFile(path): Charge un fichier JONX pour accès colonne par colonne-
get_column(field_name): Récupère une colonne décompressée -
find_min(field_name, use_index=False): Trouve la valeur minimale (avec support d'index) - Propriétés :
fields,types,indexes
-
Convertit un fichier JSON en fichier JONX.
Paramètres :
-
json_path(str) : Chemin vers le fichier JSON source -
jonx_path(str) : Chemin vers le fichier JONX de destination
Exemple :
from jsonplusplus import jonx_encode
jonx_encode("data.json", "data.jonx")Encode des données JSON en mémoire en bytes JONX.
Paramètres :
-
json_data(list) : Liste d'objets JSON (tous les objets doivent avoir les mêmes clés)
Retourne :
-
bytes: Données JONX encodées
Exemple :
from jsonplusplus import encode_to_bytes
data = [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
]
jonx_bytes = encode_to_bytes(data)Décode des bytes JONX et retourne un dictionnaire avec les données reconstruites.
Paramètres :
-
data(bytes) : Données JONX à décoder
Retourne :
-
dictavec les clés suivantes :-
version(int) : Version du format JONX -
fields(list) : Liste des noms de colonnes -
types(dict) : Dictionnaire des types par colonne -
num_rows(int) : Nombre de lignes -
json_data(list) : Données JSON reconstruites (liste d'objets)
-
Exemple :
from jsonplusplus import decode_from_bytes
with open("data.jonx", "rb") as f:
result = decode_from_bytes(f.read())
print(result["json_data"]) # Liste d'objets JSON
print(result["fields"]) # ["id", "name", ...]
print(result["types"]) # {"id": "int32", "name": "str", ...}La classe JONXFile permet un accès optimisé aux fichiers JONX avec chargement paresseux des colonnes.
JONXFile(path: str)Paramètres :
-
path(str) : Chemin vers le fichier JONX
Propriétés disponibles :
-
fields(list) : Liste des noms de colonnes disponibles -
types(dict) : Dictionnaire des types par colonne -
indexes(dict) : Dictionnaire des index disponibles (clés = noms de colonnes numériques)
Récupère une colonne décompressée. La décompression se fait à la demande (lazy loading).
Paramètres :
-
field_name(str) : Nom de la colonne à récupérer
Retourne :
-
list: Liste des valeurs de la colonne
Exemple :
file = JONXFile("data.jonx")
prices = file.get_column("price") # Décompression à la demandeRécupère plusieurs colonnes en une seule opération.
Paramètres :
-
field_names(list) : Liste des noms de colonnes à récupérer
Retourne :
-
dict: Dictionnaire {nom_colonne: [valeurs]}
Exemple :
file = JONXFile("data.jonx")
columns = file.get_columns(["id", "name", "price"])
# Retourne: {"id": [1, 2, 3], "name": ["Alice", "Bob", "Charlie"], "price": [100, 200, 300]}Trouve la valeur minimale d'une colonne.
Paramètres :
-
field(str) : Nom de la colonne -
column(list, optionnel) : Colonne pré-chargée (récupérée automatiquement si None) -
use_index(bool) : Utiliser l'index pour une recherche O(1) (recommandé pour colonnes numériques)
Retourne :
- Valeur minimale de la colonne
Exemple :
file = JONXFile("data.jonx")
min_price = file.find_min("price", use_index=True) # Ultra-rapide avec indexTrouve la valeur maximale d'une colonne.
Paramètres :
-
field(str) : Nom de la colonne -
column(list, optionnel) : Colonne pré-chargée (récupérée automatiquement si None) -
use_index(bool) : Utiliser l'index pour une recherche O(1) (recommandé pour colonnes numériques)
Retourne :
- Valeur maximale de la colonne
Exemple :
file = JONXFile("data.jonx")
max_price = file.find_max("price", use_index=True) # Ultra-rapide avec indexCalcule la somme d'une colonne numérique.
Paramètres :
-
field(str) : Nom de la colonne -
column(list, optionnel) : Colonne pré-chargée (récupérée automatiquement si None)
Retourne :
- Somme des valeurs de la colonne
Lève :
-
TypeError: Si la colonne n'est pas numérique
Exemple :
file = JONXFile("data.jonx")
total_sales = file.sum("sales")Calcule la moyenne d'une colonne numérique.
Paramètres :
-
field(str) : Nom de la colonne -
column(list, optionnel) : Colonne pré-chargée (récupérée automatiquement si None)
Retourne :
- Moyenne des valeurs de la colonne
Lève :
-
TypeError: Si la colonne n'est pas numérique -
ValueError: Si la colonne est vide
Exemple :
file = JONXFile("data.jonx")
avg_price = file.avg("price")Compte le nombre d'éléments dans une colonne ou le nombre total de lignes.
Paramètres :
-
field(str, optionnel) : Nom de la colonne (si None, retourne le nombre total de lignes)
Retourne :
- Nombre d'éléments dans la colonne ou nombre total de lignes
Exemple :
file = JONXFile("data.jonx")
total_rows = file.count() # Nombre total de lignes
price_count = file.count("price") # Nombre d'éléments dans la colonne priceRetourne un dictionnaire avec toutes les métadonnées du fichier JONX.
Retourne :
-
dictavec les clés suivantes :-
path(str) : Chemin du fichier -
version(int) : Version du format JONX -
num_rows(int) : Nombre de lignes -
num_columns(int) : Nombre de colonnes -
fields(list) : Liste des noms de colonnes -
types(dict) : Dictionnaire des types par colonne -
indexes(list) : Liste des colonnes avec index -
file_size(int) : Taille du fichier en bytes
-
Exemple :
file = JONXFile("data.jonx")
metadata = file.info()
print(f"Fichier: {metadata['path']}")
print(f"Lignes: {metadata['num_rows']}")
print(f"Colonnes: {metadata['num_columns']}")
print(f"Taille: {metadata['file_size']} bytes")Vérifie si une colonne a un index disponible.
Paramètres :
-
field(str) : Nom de la colonne à vérifier
Retourne :
-
bool: True si la colonne a un index, False sinon
Raises :
-
JONXValidationError: Si la colonne n'existe pas
Exemple :
file = JONXFile("data.jonx")
if file.has_index("price"):
print("La colonne 'price' a un index")Vérifie si une colonne est de type numérique.
Paramètres :
-
field(str) : Nom de la colonne à vérifier
Retourne :
-
bool: True si la colonne est numérique, False sinon
Raises :
-
JONXValidationError: Si la colonne n'existe pas
Exemple :
file = JONXFile("data.jonx")
if file.is_numeric("price"):
total = file.sum("price")Vérifie la cohérence du schéma du fichier JONX.
Retourne :
-
dictavec les clés suivantes :-
valid(bool) : True si le schéma est valide -
errors(list) : Liste des erreurs trouvées -
warnings(list) : Liste des avertissements
-
Exemple :
file = JONXFile("data.jonx")
schema_check = file.check_schema()
if not schema_check["valid"]:
print("Erreurs de schéma:", schema_check["errors"])Valide l'intégrité complète du fichier JONX. Effectue une validation approfondie en vérifiant le schéma, l'intégrité des données, et en tentant de décompresser toutes les colonnes.
Retourne :
-
dictavec les clés suivantes :-
valid(bool) : True si le fichier est valide -
errors(list) : Liste des erreurs trouvées -
warnings(list) : Liste des avertissements
-
Raises :
-
JONXFileError: Si le fichier ne peut pas être lu -
JONXDecodeError: Si le fichier est corrompu
Exemple :
file = JONXFile("data.jonx")
validation = file.validate()
if validation["valid"]:
print(" Fichier valide")
else:
print(" Erreurs:", validation["errors"])
if validation["warnings"]:
print(" Avertissements:", validation["warnings"])| Opération | Type | Description | Performance |
|---|---|---|---|
jonx_encode() |
Encodage | Convertit fichier JSON → JONX | O(n) |
encode_to_bytes() |
Encodage | Encode données JSON → bytes JONX | O(n) |
decode_from_bytes() |
Décodage | Décode bytes JONX → JSON complet | O(n) |
JONXFile() |
Chargement | Charge fichier JONX (lazy) | O(1) |
get_column() |
Accès | Récupère une colonne (décompression à la demande) | O(n) |
get_columns() |
Accès | Récupère plusieurs colonnes | O(n×m) |
find_min() |
Recherche | Valeur minimale (avec index = O(1)) | O(1) avec index, O(n) sans |
find_max() |
Recherche | Valeur maximale (avec index = O(1)) | O(1) avec index, O(n) sans |
sum() |
Agrégation | Somme d'une colonne numérique | O(n) |
avg() |
Agrégation | Moyenne d'une colonne numérique | O(n) |
count() |
Agrégation | Nombre d'éléments | O(1) |
info() |
Utilitaire | Métadonnées complètes du fichier | O(1) |
has_index() |
Utilitaire | Vérifie si une colonne a un index | O(1) |
is_numeric() |
Utilitaire | Vérifie si une colonne est numérique | O(1) |
check_schema() |
Utilitaire | Vérifie la cohérence du schéma | O(n) |
validate() |
Utilitaire | Valide l'intégrité complète | O(n) |
Légende :
-
n= nombre de lignes -
m= nombre de colonnes à récupérer
jsonplusplus inclut une interface en ligne de commande complète pour convertir, inspecter et interroger les fichiers JONX.
Après installation avec pip install jsonplusplus, la commande jsonplusplus (ou jonx) est disponible dans votre terminal.
# Encoder un fichier JSON
jsonplusplus encode data.json -o data.jonx
# Ou sans spécifier la sortie (génère automatiquement data.jonx)
jsonplusplus encode data.jsonOptions :
-
input: Fichier JSON d'entrée (requis) -
-o, --output: Fichier JONX de sortie (optionnel, généré automatiquement si omis)
Exemple de sortie :
Encodage de 'data.json' vers 'data.jonx'...
JONX créé : 1000 lignes, 5 colonnes
Encodage réussi!
Taille originale: 125,340 bytes
Taille JONX: 45,230 bytes
Compression: 63.9%
# Décoder un fichier JONX
jsonplusplus decode data.jonx -o data.json
# Ou sans spécifier la sortie (génère automatiquement data.json)
jsonplusplus decode data.jonxOptions :
-
input: Fichier JONX d'entrée (requis) -
-o, --output: Fichier JSON de sortie (optionnel, généré automatiquement si omis)
Exemple de sortie :
Décodage de 'data.jonx' vers 'data.json'...
Décodage réussi!
Version: 1
Lignes: 1000
Colonnes: 5
Fichier créé: data.json
jsonplusplus info data.jonxAffiche toutes les métadonnées du fichier JONX.
Exemple de sortie :
Informations sur 'data.jonx':
============================================================
Chemin: data.jonx
Version: 1
Nombre de lignes: 1,000
Nombre de colonnes: 5
Taille du fichier: 45,230 bytes
Colonnes (5):
[✓] id (int16)
[ ] name (str)
[✓] age (int16)
[✓] salary (float16)
[ ] active (bool)
Index disponibles (3):
- id
- age
- salary
jsonplusplus validate data.jonxValide l'intégrité complète du fichier JONX.
Exemple de sortie :
Validation de 'data.jonx'...
Fichier valide!
# Trouver la valeur minimale
jsonplusplus query data.jonx price --min
# Trouver la valeur maximale
jsonplusplus query data.jonx age --max --use-index
# Calculer la somme
jsonplusplus query data.jonx salary --sum
# Calculer la moyenne
jsonplusplus query data.jonx salary --avg
# Compter les éléments
jsonplusplus query data.jonx id --countOptions :
-
file: Fichier JONX (requis) -
column: Nom de la colonne (requis) -
--min: Trouver la valeur minimale -
--max: Trouver la valeur maximale -
--sum: Calculer la somme (colonne numérique uniquement) -
--avg: Calculer la moyenne (colonne numérique uniquement) -
--count: Compter les éléments -
--use-index: Utiliser l'index pour les opérations min/max (plus rapide)
Exemples de sortie :
Minimum de 'price': 10.5
Maximum de 'age': 65
Somme de 'salary': 262016.0
Moyenne de 'salary': 52403.2
Nombre d'éléments dans 'id': 1000
Pour voir toutes les commandes disponibles :
jsonplusplus --helpPour voir l'aide d'une commande spécifique :
jsonplusplus encode --help
jsonplusplus query --helpVous pouvez aussi utiliser le CLI via Python :
python -m jsonplusplus encode data.json
python -m jsonplusplus info data.jonxOuvre une application desktop moderne pour visualiser les fichiers JONX.
# Ouvrir le visualiseur
jsonplusplus view
# Ouvrir directement un fichier
jsonplusplus view data.jonx
# Ou utiliser la commande dédiée
jonx-viewer data.jonxFonctionnalités du visualiseur :
- Interface moderne avec mode sombre/clair
- Tableau interactif avec pagination
- Recherche en temps réel
- Métadonnées et statistiques
- Export CSV/JSON
- Statistiques automatiques (min, max, avg)
Installation du support GUI :
pip install jsonplusplus[gui]
# Ou
pip install customtkinterVoir VIEWER_GUI.md pour la documentation complète du visualiseur.
from jsonplusplus import jonx_encode, decode_from_bytes
# Encoder un fichier JSON en JONX
jonx_encode("data.json", "data.jonx")
# Décoder depuis bytes
with open("data.jonx", "rb") as f:
result = decode_from_bytes(f.read())
print(result["json_data"][0])
print(f"Colonnes: {result['fields']}")
print(f"Types: {result['types']}")from jsonplusplus import JONXFile
# Charger un fichier JONX
file = JONXFile("data.jonx")
# Accéder aux métadonnées
print(f"Colonnes disponibles: {file.fields}")
print(f"Types détectés: {file.types}")
print(f"Index disponibles: {list(file.indexes.keys())}")
# Récupérer une colonne spécifique (décompression à la demande)
ages = file.get_column("age")
prices = file.get_column("price")
# Récupérer plusieurs colonnes en une fois
columns = file.get_columns(["id", "name", "price"])
# Utiliser les index pour des recherches ultra-rapides
min_age = file.find_min("age", use_index=True)
max_price = file.find_max("price", use_index=True)
# Opérations d'agrégation
total_sales = file.sum("sales")
avg_price = file.avg("price")
num_rows = file.count()
print(f"Âge minimum: {min_age}")
print(f"Prix maximum: {max_price}")
print(f"Total ventes: {total_sales}")
print(f"Prix moyen: {avg_price}")
print(f"Nombre de lignes: {num_rows}")
# Reconstruire le JSON complet si nécessaire
json_data = []
num_rows = len(ages)
for i in range(num_rows):
obj = {field: file.get_column(field)[i] for field in file.fields}
json_data.append(obj)from jsonplusplus import encode_to_bytes, decode_from_bytes
# Données JSON en mémoire
data = [
{"id": 1, "name": "Alice", "age": 30, "salary": 50000.5, "active": True},
{"id": 2, "name": "Bob", "age": 25, "salary": 45000.0, "active": False},
{"id": 3, "name": "Charlie", "age": 35, "salary": 60000.75, "active": True}
]
# Encoder en bytes JONX
jonx_bytes = encode_to_bytes(data)
# Sauvegarder ou transmettre
with open("output.jonx", "wb") as f:
f.write(jonx_bytes)
# Décoder plus tard
result = decode_from_bytes(jonx_bytes)
print(f"Encodé {result['num_rows']} lignes avec {len(result['fields'])} colonnes")Le format JONX est structuré de manière séquentielle pour permettre une lecture efficace :
┌─────────────────────────────────────────────────────────────┐
│ HEADER (8 bytes) │
├─────────────────────────────────────────────────────────────┤
│ Signature: "JONX" (4 bytes) │
│ Version: uint32 (4 bytes) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ SCHÉMA COMPRESSÉ │
├─────────────────────────────────────────────────────────────┤
│ Taille: uint32 (4 bytes) │
│ Données compressées (zstd): {fields: [...], types: {...}} │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ COLONNES COMPRESSÉES (pour chaque colonne) │
├─────────────────────────────────────────────────────────────┤
│ Taille: uint32 (4 bytes) │
│ Données compressées (zstd): colonne binaire ou JSON │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ INDEX COMPRESSÉS (optionnels) │
├─────────────────────────────────────────────────────────────┤
│ Nombre d'index: uint32 (4 bytes) │
│ Pour chaque index: │
│ ├── Taille du nom: uint32 (4 bytes) │
│ ├── Nom du champ (UTF-8) │
│ ├── Taille de l'index: uint32 (4 bytes) │
│ └── Index compressé (zstd): indices triés │
└─────────────────────────────────────────────────────────────┘
| Type | Description | Stockage |
|---|---|---|
int16 |
Entiers 16 bits (-32768 à 32767) | Binaire (2 bytes/valeur) |
int32 |
Entiers 32 bits | Binaire (4 bytes/valeur) |
float16 |
Flottants 16 bits (IEEE 754) | Binaire (2 bytes/valeur) |
float32 |
Flottants 32 bits (IEEE 754) | Binaire (4 bytes/valeur) |
bool |
Booléens | Binaire (1 byte/valeur) |
str |
Chaînes de caractères | JSON compressé (zstd) |
json |
Objets complexes | JSON compressé (zstd) |
La bibliothèque détecte automatiquement le type optimal pour chaque colonne :
-
Entiers :
int16si toutes les valeurs sont dans [-32768, 32767], sinonint32 -
Flottants :
float16si précision ≤ 3 décimales et dans la plage IEEE 754, sinonfloat32 - Booléens : Détectés automatiquement
-
Chaînes : Stockées comme
str(JSON compressé) -
Objets complexes : Stockés comme
json(JSON compressé)
Les colonnes numériques (int16, int32, float16, float32) génèrent automatiquement un index trié compressé, permettant des recherches min/max en O(1) après décompression de l'index.
Les données sont reconstruites ligne par ligne en combinant les colonnes décompressées selon l'ordre des champs dans le schéma.
Grâce à la combinaison du stockage en colonnes et de la compression Zstandard, JONX peut réduire la taille des fichiers de 50% à 80% par rapport au JSON brut, selon la structure des données.
Contrairement au JSON qui doit charger toutes les données, JONX permet de décompresser uniquement les colonnes nécessaires, réduisant significativement l'utilisation de la RAM pour les datasets volumineux.
- Analytics : Accès rapide aux colonnes numériques avec index
- Machine Learning : Chargement sélectif des features nécessaires
- Datasets volumineux : Compression efficace et lecture paresseuse
Aucune dépendance externe lourde. Utilise uniquement des bibliothèques Python standard et des bindings optimisés (orjson, zstandard, numpy).
- Encodage/décodage JSON ↔ JONX
- Auto-détection des types (int16, int32, float16, float32, bool, str, json)
- Compression Zstandard
- Index automatiques pour colonnes numériques
- Classe
JONXFileavec accès colonne par colonne - Support des recherches min/max avec index
- Opérations d'agrégation (sum, avg, count)
- Récupération multiple de colonnes (get_columns)
- Support des types additionnels (int8, int64, float64)
- Index personnalisés (multi-colonnes)
- Filtrage et projection de colonnes optimisés
- Support des données nulles (NULL handling)
- Streaming pour fichiers volumineux
- API de requête simple (filtres, groupby, joins)
- Opérations d'agrégation avancées (std, median, quantiles)
- Benchmarks de performance complets
- Support multi-fichiers (partitionnement)
- Compression adaptative (choix du niveau zstd par colonne)
- Métadonnées étendues (statistiques, cardinalité)
- Intégration avec pandas/Polars
- Support des types temporels (date, datetime, timestamp)
- Compression différentielle pour séries temporelles
- API de requête avancée (base de donnée)
Ce projet est sous licence MIT. Voir le fichier LICENSE pour plus de détails.
Les contributions sont les bienvenues ! Voici comment contribuer :
- Fork le projet
- Créez une branche pour votre feature (
git checkout -b feature/AmazingFeature) -
Commit vos changements (
git commit -m 'Add some AmazingFeature') -
Push vers la branche (
git push origin feature/AmazingFeature) - Ouvrez une Pull Request
-
Formatage : Utilisez
blackpour le formatage du code -
Linting : Respectez
ruffouflake8pour le linting - Tests : Ajoutez des tests pour toute nouvelle fonctionnalité
- Documentation : Mettez à jour la documentation si nécessaire
- Type hints : Utilisez les annotations de type Python 3.8+
jsonplusplus/
├── src/
│ └── jsonplusplus/
│ ├── __init__.py
│ ├── encoder.py # Encodage JSON → JONX
│ └── decoder.py # Décodage JONX → JSON
├── tests/ # Tests unitaires
├── README.md
├── pyproject.toml
└── LICENSE
Ouvrez une issue avec :
- Description du bug
- Étapes pour reproduire
- Comportement attendu vs comportement actuel
- Version de Python et de la bibliothèque
Nathan Josué
- GitHub: @Nathan-Josue
- Projet: jsonplusplus
-
orjsonpour le parsing JSON ultra-rapide -
zstandardpour la compression efficace - Inspiré par les formats colonnaires modernes (Apache Parquet, Apache Arrow)
⭐ Si ce projet vous est utile, n'hésitez pas à lui donner une étoile sur GitHub !