polars-jsonschema-bridge

Crates.iopolars-jsonschema-bridge
lib.rspolars-jsonschema-bridge
version0.4.0
created_at2025-08-20 15:13:20.059673+00
updated_at2025-09-25 10:58:31.380532+00
descriptionBidirectional conversion between JSON Schema and Polars DataTypes
homepagehttps://github.com/lmmx/polars-genson
repositoryhttps://github.com/lmmx/polars-genson
max_upload_size
id1803542
size151,368
Louis Maddox (lmmx)

documentation

https://docs.rs/polars-jsonschema-bridge/

README

Polars JSON Schema Bridge

crates.io MIT/Apache-2.0 licensed Documentation

A Rust library for bidirectional conversion between JSON Schema and Polars data types.

This crate provides the core type conversion logic used by the polars-genson plugin, but can also be used independently for any application that needs to convert between JSON Schema and Polars types.

In addition, the crate supports Avro → Polars conversion, enabling Polars schema inference from Avro record schemas (including maps, unions, and nested records).

Features

  • JSON Schema → Polars: Convert JSON Schema type definitions to Polars DataType
  • Avro → Polars: Convert Avro record schemas to Polars DataType
  • Polars → JSON Schema: Convert Polars schemas to valid JSON Schema documents
  • Complex Types: Support for nested objects, arrays, and structs
  • Type Safety: Comprehensive error handling for unsupported conversions
  • Standards Compliant: Generates JSON Schema following Draft 2020-12 specification

Usage

Add this to your Cargo.toml:

[dependencies]
polars-jsonschema-bridge = "0.1.1"
polars = "0.50"
serde_json = "1.0"

JSON Schema to Polars Types

use polars_jsonschema_bridge::{schema_to_polars_fields, SchemaFormat};
use serde_json::json;

let json_schema = json!({
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer"},
        "scores": {
            "type": "array",
            "items": {"type": "number"}
        },
        "profile": {
            "type": "object",
            "properties": {
                "active": {"type": "boolean"}
            }
        }
    }
});

let fields = schema_to_polars_fields(&json_schema, SchemaFormat::JsonSchema, false)?;
// Returns: [
//   ("name", "String"),
//   ("age", "Int64"), 
//   ("scores", "List[Float64]"),
//   ("profile", "Struct[active:Boolean]")
// ]

Avro Schema to Polars Types

use polars_jsonschema_bridge::{schema_to_polars_fields, SchemaFormat};
use serde_json::json;

let avro_schema = json!({
    "type": "record",
    "name": "document",
    "fields": [
        {"name": "id", "type": "int"},
        {"name": "tags", "type": ["null", {"type": "array", "items": "string"}]},
        {"name": "labels", "type": {"type": "map", "values": "string"}},
        {"name": "active", "type": ["null", "boolean"]}
    ]
});

let fields = schema_to_polars_fields(&avro_schema, SchemaFormat::Avro, false)?;
// Returns: [
//   ("id", "Int64"),
//   ("tags", "List[String]"),
//   ("labels", "List[Struct[key:String,value:String]]"),
//   ("active", "Boolean")
// ]

Polars Schema to JSON Schema

use polars_jsonschema_bridge::{polars_schema_to_json_schema, JsonSchemaOptions};
use polars::prelude::*;

let mut schema = Schema::default();
schema.with_column("name".into(), DataType::String);
schema.with_column("age".into(), DataType::Int64);
schema.with_column("scores".into(), DataType::List(Box::new(DataType::Float64)));

// Use default options
let json_schema = polars_schema_to_json_schema(&schema, &JsonSchemaOptions::new())?;

// Or customize the output with options
let options = JsonSchemaOptions::new()
    .with_title(Some("User Schema"))
    .with_description(Some("A simple user record"))
    .with_optional_fields(vec!["email"])
    .with_additional_properties(true);

let json_schema_custom = polars_schema_to_json_schema(&schema, &options)?;

This generates a JSON Schema document with the desired metadata and field requirements.

Individual Type Conversions

use polars_jsonschema_bridge::{json_type_to_polars_type, polars_dtype_to_json_schema, JsonSchemaOptions};
use polars::prelude::DataType;
use serde_json::json;

// JSON Schema type → Polars type string
let polars_type = json_type_to_polars_type(&json!({"type": "string"}))?;
assert_eq!(polars_type, "String");

// Polars DataType → JSON Schema
let json_schema = polars_dtype_to_json_schema(
    &DataType::List(Box::new(DataType::Int64)),
    &JsonSchemaOptions::default()
)?;
assert_eq!(json_schema, json!({
    "type": "array",
    "items": {"type": "integer"}
}));

Supported Type Mappings

JSON Schema → Polars

JSON Schema Type Polars DataType Notes
string String
integer Int64
number Float64
boolean Boolean
null Null
array List[T] Where T is the items' type
object Struct[...] Nested object properties
  • Note that we do not have JSON Schema array to Polars Array conversion (...yet?)

Avro → Polars

Avro Type Polars DataType Notes
"string" String
"int", "long" Int64 All integers coerced to 64-bit
"float", "double" Float64
"boolean" Boolean
"null" Null
{"type": "array", "items": T} List[T] T converted recursively
{"type": "map", "values": T} List[Struct[key:String,value:T]] Encoded as key/value structs
{"type": "record", "fields": [...]} Struct[...] Each field converted recursively
[ "null", T, ... ] T Union: first non-null branch is chosen

Polars → JSON Schema

Polars DataType JSON Schema Notes
Boolean {"type": "boolean"}
Int8/16/32/64/128 {"type": "integer"}
UInt8/16/32/64 {"type": "integer", "minimum": 0}
Float32/64 {"type": "number"}
String {"type": "string"}
Date {"type": "string", "format": "date"}
Datetime {"type": "string", "format": "date-time"}
List[T] {"type": "array", "items": {...}}
Array[T, N] {"type": "array", "minItems": N, "maxItems": N}
Struct[...] {"type": "object", "properties": {...}}

Error Handling

The library provides comprehensive error handling through Polars' PolarsError type:

use polars_jsonschema_bridge::json_type_to_polars_type;
use serde_json::json;

// Unsupported types return errors
let result = json_type_to_polars_type(&json!({"type": "unsupported"}));
assert!(result.is_err());

JSON Schema Generation Options

The JsonSchemaOptions struct lets you control aspects of the generated schema:

Option Default Effect
schema_uri Some("https://json-schema.org/draft/2020-12/schema") Controls the $schema field (or omit entirely with None)
title None Adds a title to the schema
description None Adds a description to the schema
optional_fields empty set By default all fields are required; use this to mark some as optional
additional_properties false Controls the additionalProperties flag

Example:

use polars_jsonschema_bridge::JsonSchemaOptions;

let options = JsonSchemaOptions::new()
    .with_title(Some("Example"))
    .with_optional_fields(vec!["email"])
    .with_additional_properties(false);

Debug Mode

Enable debug output to see the intermediate JSON Schema during conversion:

use polars_jsonschema_bridge::{schema_to_polars_fields, JsonSchema};

let fields = schema_to_polars_fields(&json_schema, SchemaFormat::JsonSchema, true)?; // debug = true
// Prints the generated JSON Schema to stderr

Integration

This crate was designed for use in polars-genson, a Python plugin for JSON schema inference

See also: genson-core, a separate crate from the same monorepo for JSON schema inference (without the Polars part)

Please feel free to use this crate in your Rust applications working with Polars and JSON Schema and submit feature requests/contributions.

Contributing

This crate is part of the polars-genson project. See the main repository for the contribution and development docs.

License

Licensed under the MIT License. See LICENSE for details.

Commit count: 166

cargo fmt