Hjson, a user interface for JSON - human-friendly JSON configuration file format with comments, unquoted keys, and multiline strings
npx @tessl/cli install tessl/pypi-hjson@3.1.0Hjson (Human JSON) is a user interface for JSON that makes configuration files more readable and maintainable. It extends JSON with comments, unquoted keys, optional commas, multiline strings, and other human-friendly features while maintaining full JSON compatibility. The library provides both Hjson parsing/generation and standard JSON encoding/decoding capabilities with an API that mirrors Python's built-in json module.
pip install hjsonimport hjsonDirect imports for common functionality:
from hjson import loads, dumps, load, dump
from hjson import dumpsJSON, dumpJSON
from hjson import HjsonDecoder, HjsonEncoder, JSONEncoder
from hjson import HjsonDecodeError, OrderedDictimport hjson
# Parse Hjson string with human-friendly syntax
hjson_text = """{
// This is a comment
name: John Doe
age: 30
'quoted key': value with spaces
multiline: '''
This is a
multiline string
'''
}"""
# Parse to Python object
data = hjson.loads(hjson_text)
print(data) # OrderedDict([('name', 'John Doe'), ('age', 30), ...])
# Convert Python object to Hjson string
hjson_output = hjson.dumps(data)
print(hjson_output)
# Convert to standard JSON
json_output = hjson.dumpsJSON(data)
print(json_output) # Standard JSON formatHjson provides dual encoding/decoding capabilities:
Parse Hjson format strings and files into Python objects with support for comments, unquoted keys, and human-friendly syntax.
def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
parse_int=None, object_pairs_hook=None, use_decimal=False, **kw):
"""
Deserialize Hjson string to Python object.
Parameters:
- s: str or bytes, Hjson document to parse
- encoding: str, character encoding (default: 'utf-8')
- cls: HjsonDecoder subclass for custom decoding
- object_hook: callable, called with result of every JSON object decoded
- parse_float: callable, used for parsing JSON floats
- parse_int: callable, used for parsing JSON integers
- object_pairs_hook: callable, called with ordered list of pairs
- use_decimal: bool, use decimal.Decimal for floats (default: False)
Returns:
Parsed Python object (typically OrderedDict)
"""
def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
parse_int=None, object_pairs_hook=OrderedDict, use_decimal=False,
namedtuple_as_object=True, tuple_as_array=True, **kw):
"""
Deserialize Hjson from file-like object to Python object.
Parameters:
- fp: file-like object with .read() method containing Hjson document
- encoding: str, character encoding (default: 'utf-8')
- cls: HjsonDecoder subclass for custom decoding
- object_hook: callable, called with result of every JSON object decoded
- parse_float: callable, used for parsing JSON floats
- parse_int: callable, used for parsing JSON integers
- object_pairs_hook: callable, called with ordered list of pairs (default: OrderedDict)
- use_decimal: bool, use decimal.Decimal for floats (default: False)
- namedtuple_as_object: bool, encode namedtuples as objects (default: True)
- tuple_as_array: bool, encode tuples as arrays (default: True)
Returns:
Parsed Python object
"""Convert Python objects to human-readable Hjson format with customizable formatting options.
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
cls=None, indent=None, encoding='utf-8', default=None,
use_decimal=True, namedtuple_as_object=True, tuple_as_array=True,
bigint_as_string=False, sort_keys=False, item_sort_key=None,
for_json=False, int_as_string_bitcount=None, **kw):
"""
Serialize Python object to Hjson formatted string.
Parameters:
- obj: Python object to serialize
- skipkeys: bool, skip non-basic dict keys (default: False)
- ensure_ascii: bool, escape non-ASCII characters (default: True)
- check_circular: bool, check for circular references (default: True)
- cls: HjsonEncoder subclass for custom encoding
- indent: int or str, indentation for pretty-printing (default: 2 spaces)
- encoding: str, character encoding (default: 'utf-8')
- default: callable, called for objects not serializable by default
- use_decimal: bool, natively serialize Decimal (default: True)
- namedtuple_as_object: bool, encode namedtuples as objects (default: True)
- tuple_as_array: bool, encode tuples as arrays (default: True)
- bigint_as_string: bool, encode large ints as strings (default: False)
- sort_keys: bool, sort dictionary output by key (default: False)
- item_sort_key: callable, custom key sorting function
- for_json: bool, use for_json() method if available (default: False)
- int_as_string_bitcount: int, bit threshold for string encoding
Returns:
Hjson formatted string
"""
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
cls=None, indent=None, encoding='utf-8', default=None,
use_decimal=True, namedtuple_as_object=True, tuple_as_array=True,
bigint_as_string=False, sort_keys=False, item_sort_key=None,
for_json=False, int_as_string_bitcount=None, **kw):
"""
Serialize Python object to Hjson formatted stream.
Parameters:
- obj: Python object to serialize
- fp: file-like object with .write() method
- skipkeys: bool, skip non-basic dict keys (default: False)
- ensure_ascii: bool, escape non-ASCII characters (default: True)
- check_circular: bool, check for circular references (default: True)
- cls: HjsonEncoder subclass for custom encoding
- indent: int or str, indentation for pretty-printing (default: 2 spaces)
- encoding: str, character encoding (default: 'utf-8')
- default: callable, called for objects not serializable by default
- use_decimal: bool, natively serialize Decimal (default: True)
- namedtuple_as_object: bool, encode namedtuples as objects (default: True)
- tuple_as_array: bool, encode tuples as arrays (default: True)
- bigint_as_string: bool, encode large ints as strings (default: False)
- sort_keys: bool, sort dictionary output by key (default: False)
- item_sort_key: callable, custom key sorting function
- for_json: bool, use for_json() method if available (default: False)
- int_as_string_bitcount: int, bit threshold for string encoding
Returns:
None (writes to file-like object)
"""Convert Python objects to standard JSON format with performance optimizations and compatibility with Python's json module.
def dumpsJSON(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
cls=None, indent=None, separators=None, encoding='utf-8',
default=None, use_decimal=True, namedtuple_as_object=True,
tuple_as_array=True, bigint_as_string=False, sort_keys=False,
item_sort_key=None, for_json=False, int_as_string_bitcount=None, **kw):
"""
Serialize Python object to JSON formatted string.
Parameters:
- obj: Python object to serialize
- skipkeys: bool, skip non-basic dict keys (default: False)
- ensure_ascii: bool, escape non-ASCII characters (default: True)
- check_circular: bool, check for circular references (default: True)
- cls: JSONEncoder subclass for custom encoding
- indent: int or str, indentation for pretty-printing
- separators: tuple, (item_separator, key_separator) for formatting
- encoding: str, character encoding (default: 'utf-8')
- default: callable, called for objects not serializable by default
- use_decimal: bool, natively serialize Decimal (default: True)
- namedtuple_as_object: bool, encode namedtuples as objects (default: True)
- tuple_as_array: bool, encode tuples as arrays (default: True)
- bigint_as_string: bool, encode large ints as strings (default: False)
- sort_keys: bool, sort dictionary output by key (default: False)
- item_sort_key: callable, custom key sorting function
- for_json: bool, use for_json() method if available (default: False)
- int_as_string_bitcount: int, bit threshold for string encoding
Returns:
JSON formatted string
"""
def dumpJSON(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
cls=None, indent=None, separators=None, encoding='utf-8',
default=None, use_decimal=True, namedtuple_as_object=True,
tuple_as_array=True, bigint_as_string=False, sort_keys=False,
item_sort_key=None, for_json=False, int_as_string_bitcount=None, **kw):
"""
Serialize Python object to JSON formatted stream.
Parameters:
- obj: Python object to serialize
- fp: file-like object with .write() method
- skipkeys: bool, skip non-basic dict keys (default: False)
- ensure_ascii: bool, escape non-ASCII characters (default: True)
- check_circular: bool, check for circular references (default: True)
- cls: JSONEncoder subclass for custom encoding
- indent: int or str, indentation for pretty-printing
- separators: tuple, (item_separator, key_separator) for formatting
- encoding: str, character encoding (default: 'utf-8')
- default: callable, called for objects not serializable by default
- use_decimal: bool, natively serialize Decimal (default: True)
- namedtuple_as_object: bool, encode namedtuples as objects (default: True)
- tuple_as_array: bool, encode tuples as arrays (default: True)
- bigint_as_string: bool, encode large ints as strings (default: False)
- sort_keys: bool, sort dictionary output by key (default: False)
- item_sort_key: callable, custom key sorting function
- for_json: bool, use for_json() method if available (default: False)
- int_as_string_bitcount: int, bit threshold for string encoding
Returns:
None (writes to file-like object)
"""Advanced classes for custom serialization and deserialization behavior.
class HjsonDecoder(object):
"""
Hjson decoder with customizable parsing behavior.
"""
def __init__(self, encoding=None, object_hook=None, object_pairs_hook=None,
parse_float=None, parse_int=None, strict=True, use_decimal=False):
"""
Initialize Hjson decoder.
Parameters:
- encoding: str, character encoding
- object_hook: callable, called with every decoded object
- object_pairs_hook: callable, called with ordered pairs
- parse_float: callable, custom float parsing
- parse_int: callable, custom int parsing
- strict: bool, strict parsing mode
- use_decimal: bool, use Decimal for floats
"""
def decode(self, s, _w=None):
"""
Decode Hjson string to Python object.
Parameters:
- s: str, Hjson string to decode
Returns:
Decoded Python object
"""
def raw_decode(self, s, idx=0):
"""
Decode Hjson from string starting at given index.
Parameters:
- s: str, Hjson string
- idx: int, starting index
Returns:
tuple: (decoded_object, end_index)
"""
class HjsonEncoder(object):
"""
Hjson encoder with customizable formatting behavior.
"""
def __init__(self, skipkeys=False, ensure_ascii=True, check_circular=True,
indent=None, encoding='utf-8', default=None, use_decimal=True,
namedtuple_as_object=True, tuple_as_array=True,
bigint_as_string=False, sort_keys=False, item_sort_key=None,
for_json=False, int_as_string_bitcount=None):
"""
Initialize Hjson encoder.
Parameters:
- skipkeys: bool, skip non-basic dict keys
- ensure_ascii: bool, escape non-ASCII characters
- check_circular: bool, check for circular references
- indent: int or str, indentation for pretty-printing
- encoding: str, character encoding
- default: callable, fallback for non-serializable objects
- use_decimal: bool, natively serialize Decimal
- namedtuple_as_object: bool, encode namedtuples as objects
- tuple_as_array: bool, encode tuples as arrays
- bigint_as_string: bool, encode large ints as strings
- sort_keys: bool, sort dictionary keys
- item_sort_key: callable, custom key sorting function
- for_json: bool, use for_json() method if available
- int_as_string_bitcount: int, bit threshold for string encoding
"""
def default(self, o):
"""
Override to implement custom encoding for objects.
Parameters:
- o: object to encode
Returns:
Serializable representation of object
Raises:
TypeError: if object is not serializable
"""
def encode(self, o):
"""
Encode Python object to Hjson string.
Parameters:
- o: object to encode
Returns:
Hjson formatted string
"""
def iterencode(self, o, _one_shot=False):
"""
Encode Python object as iterator of string chunks.
Parameters:
- o: object to encode
- _one_shot: bool, internal optimization flag
Yields:
str: chunks of encoded Hjson
"""
class JSONEncoder(object):
"""
JSON encoder compatible with Python's json module.
"""
def __init__(self, skipkeys=False, ensure_ascii=True, check_circular=True,
indent=None, separators=None, encoding='utf-8', default=None,
use_decimal=True, namedtuple_as_object=True, tuple_as_array=True,
bigint_as_string=False, sort_keys=False, item_sort_key=None,
for_json=False, int_as_string_bitcount=None):
"""
Initialize JSON encoder.
Parameters:
- skipkeys: bool, skip non-basic dict keys
- ensure_ascii: bool, escape non-ASCII characters
- check_circular: bool, check for circular references
- indent: int or str, indentation for pretty-printing
- separators: tuple, (item_separator, key_separator)
- encoding: str, character encoding
- default: callable, fallback for non-serializable objects
- use_decimal: bool, natively serialize Decimal
- namedtuple_as_object: bool, encode namedtuples as objects
- tuple_as_array: bool, encode tuples as arrays
- bigint_as_string: bool, encode large ints as strings
- sort_keys: bool, sort dictionary keys
- item_sort_key: callable, custom key sorting function
- for_json: bool, use for_json() method if available
- int_as_string_bitcount: int, bit threshold for string encoding
"""
def default(self, o):
"""
Override to implement custom encoding for objects.
Parameters:
- o: object to encode
Returns:
Serializable representation of object
Raises:
TypeError: if object is not serializable
"""
def encode(self, o):
"""
Encode Python object to JSON string.
Parameters:
- o: object to encode
Returns:
JSON formatted string
"""
def iterencode(self, o, _one_shot=False):
"""
Encode Python object as iterator of string chunks.
Parameters:
- o: object to encode
- _one_shot: bool, internal optimization flag
Yields:
str: chunks of encoded JSON
"""Helper functions and data structures for enhanced functionality.
def simple_first(kv):
"""
Helper function for item_sort_key to sort simple elements first.
Sorts simple data types (strings, numbers, booleans, None) before
container types (lists, dicts, tuples) in dictionary output.
Parameters:
- kv: tuple, (key, value) pair from dictionary
Returns:
tuple: sort key tuple for ordering
"""
class OrderedDict(dict):
"""
Dictionary that preserves insertion order of keys.
Used as default object_pairs_hook to maintain key ordering
when parsing Hjson/JSON documents. Provides full dict interface
with ordering preservation.
"""
def __init__(self, *args, **kwargs):
"""Initialize ordered dictionary."""
# Standard dict methods with ordering preservation
def __setitem__(self, key, value): ...
def __delitem__(self, key): ...
def __iter__(self): ...
def keys(self): ...
def values(self): ...
def items(self): ...
def popitem(self, last=True): ...
def move_to_end(self, key, last=True): ...
class HjsonDecodeError(ValueError):
"""
Exception raised when Hjson decoding fails.
Provides detailed error information including position
and context for debugging malformed Hjson documents.
"""
def __init__(self, msg, doc, pos):
"""
Initialize decode error.
Parameters:
- msg: str, error message
- doc: str, source document
- pos: int, character position of error
"""
@property
def lineno(self):
"""int: Line number where error occurred."""
@property
def colno(self):
"""int: Column number where error occurred."""
@property
def msg(self):
"""str: Error message."""
@property
def doc(self):
"""str: Source document."""
@property
def pos(self):
"""int: Character position of error."""Hjson includes a command-line tool for converting between Hjson and JSON formats.
After installing the package with pip install hjson, the hjson command becomes available:
# Convert Hjson to formatted Hjson (default)
echo '{name: value}' | hjson
# Convert to formatted JSON
echo '{name: value}' | hjson -j
# Convert to compact JSON
echo '{name: value}' | hjson -c
# Process files
hjson input.hjson
hjson -j input.hjson > output.json
# Show help
hjson --help
# Show version
hjson --version-j: Output as formatted JSON with indentation-c: Output as compact JSON without extra whitespace-h, --help: Show help message-V, --version: Show version informationimport hjson
from decimal import Decimal
# Custom parsing with Decimal for high precision
def decimal_hook(pairs):
result = {}
for key, value in pairs:
if isinstance(value, float):
result[key] = Decimal(str(value))
else:
result[key] = value
return result
data = hjson.loads('{"price": 19.99}', object_pairs_hook=decimal_hook)
print(type(data['price'])) # <class 'decimal.Decimal'>import hjson
from datetime import datetime
class DateTimeEncoder(hjson.HjsonEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)
data = {'timestamp': datetime.now(), 'value': 42}
result = hjson.dumps(data, cls=DateTimeEncoder)
print(result)import hjson
# OrderedDict is used by default for object_pairs_hook
hjson_text = """{
z: last
a: first
m: middle
}"""
data = hjson.loads(hjson_text)
print(list(data.keys())) # ['z', 'a', 'm'] - preserves original orderimport hjson
try:
# Invalid Hjson syntax
data = hjson.loads('{invalid syntax}')
except hjson.HjsonDecodeError as e:
print(f"Parse error at line {e.lineno}, column {e.colno}: {e.msg}")
print(f"Position {e.pos} in document")import hjson
import os
def load_config(config_path):
"""Load configuration from Hjson file with environment variable support."""
with open(config_path, 'r') as f:
config_text = f.read()
# Parse Hjson configuration
config = hjson.load(f)
# Override with environment variables
if 'DATABASE_URL' in os.environ:
config['database']['url'] = os.environ['DATABASE_URL']
return config
# config.hjson:
# {
# // Database configuration
# database: {
# url: localhost:5432
# pool_size: 10
# }
# // Feature flags
# features: {
# caching: true
# analytics: false
# }
# }
config = load_config('config.hjson')
print(hjson.dumps(config, indent=2))