A "no badge / not found" state on any of the registry badges above means the corresponding package has not been published yet for that version. The badges read live from the package registries, so they always reflect the latest published state.
Universal serialization library to encode/decode objects to/from Links Notation format. Available in Python, JavaScript, Rust, and C# with identical functionality and API design.
This library provides universal serialization and deserialization with built-in support for circular references and complex object graphs in:
- Python - Full implementation for Python 3.8+
- JavaScript - Full implementation for Node.js 18+
- Rust - Full implementation for Rust 1.70+
- C# - Full implementation for .NET 8.0+
All implementations share the same design philosophy and provide feature parity.
- Universal Serialization: Encode objects to Links Notation format
- Type Support: Handle all common types in each language:
- Python:
None,bool,int,float,str,list,dict - JavaScript:
null,undefined,boolean,number,string,Array,Object - Rust:
LinoValueenum withNull,Bool,Int,Float,String,Array,Object - C#:
null,bool,int,long,float,double,string,List<object?>,Dictionary<string, object?> - Special float/number values:
NaN,Infinity,-Infinity
- Python:
- Circular References: Automatically detect and preserve circular references
- Object Identity: Maintain object identity for shared references
- UTF-8 Support: Full Unicode string support using base64 encoding
- Simple API: Easy-to-use
encode()anddecode()functions - JSON/Lino Conversion: Convert between JSON and Links Notation (JavaScript)
- Reference Escaping: Properly escape strings for Links Notation format (JavaScript)
- Fuzzy Matching: String similarity utilities for finding matches (JavaScript)
- Indented Format: Human-readable indented Links Notation format for display and debugging
pip install lino-objects-codecfrom link_notation_objects_codec import encode, decode
# Encode and decode
data = {"name": "Alice", "age": 30, "active": True}
encoded = encode(data)
decoded = decode(encoded)
assert decoded == datanpm install lino-objects-codecimport { encode, decode } from 'lino-objects-codec';
// Encode and decode
const data = { name: 'Alice', age: 30, active: true };
const encoded = encode(data);
const decoded = decode(encoded);
console.log(JSON.stringify(decoded) === JSON.stringify(data)); // true[dependencies]
lino-objects-codec = "0.1"use lino_objects_codec::{encode, decode, LinoValue};
// Encode and decode
let data = LinoValue::object([
("name", LinoValue::String("Alice".to_string())),
("age", LinoValue::Int(30)),
("active", LinoValue::Bool(true)),
]);
let encoded = encode(&data);
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, data);dotnet add package Lino.Objects.Codecusing Lino.Objects.Codec;
// Encode and decode
var data = new Dictionary<string, object?>
{
{ "name", "Alice" },
{ "age", 30 },
{ "active", true }
};
var encoded = Codec.Encode(data);
var decoded = Codec.Decode(encoded) as Dictionary<string, object?>;
Console.WriteLine(decoded?["name"]); // Alice.
├── python/ # Python implementation
│ ├── src/ # Source code
│ ├── tests/ # Test suite
│ ├── examples/ # Usage examples
│ └── README.md # Python-specific docs
├── js/ # JavaScript implementation
│ ├── src/ # Source code
│ ├── tests/ # Test suite
│ ├── examples/ # Usage examples
│ └── README.md # JavaScript-specific docs
├── rust/ # Rust implementation
│ ├── src/ # Source code
│ ├── examples/ # Usage examples
│ └── README.md # Rust-specific docs
├── csharp/ # C# implementation
│ ├── src/ # Source code
│ ├── tests/ # Test suite
│ ├── examples/ # Usage examples
│ └── README.md # C#-specific docs
└── README.md # This file
For detailed documentation, API reference, and examples, see:
All implementations support the same features with language-appropriate syntax:
Python:
from link_notation_objects_codec import encode, decode
# Self-referencing list
lst = [1, 2, 3]
lst.append(lst)
decoded = decode(encode(lst))
assert decoded[3] is decoded # Reference preservedJavaScript:
import { encode, decode } from 'lino-objects-codec';
// Self-referencing array
const arr = [1, 2, 3];
arr.push(arr);
const decoded = decode(encode(arr));
console.log(decoded[3] === decoded); // true - Reference preservedRust:
use lino_objects_codec::{encode, decode, LinoValue};
// Self-referencing structures are handled via object IDs
let data = LinoValue::array([LinoValue::Int(1), LinoValue::Int(2)]);
let encoded = encode(&data);
let decoded = decode(&encoded).unwrap();
// Reference semantics preserved through encoding/decodingC#:
using Lino.Objects.Codec;
// Self-referencing list
var lst = new List<object?>();
lst.Add(lst);
var decoded = Codec.Decode(Codec.Encode(lst)) as List<object?>;
Console.WriteLine(ReferenceEquals(decoded, decoded?[0])); // True - Reference preservedPython:
data = {
"users": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
],
"metadata": {"version": 1, "count": 2}
}
assert decode(encode(data)) == dataJavaScript:
const data = {
users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
],
metadata: { version: 1, count: 2 }
};
console.log(JSON.stringify(decode(encode(data))) === JSON.stringify(data));Rust:
use lino_objects_codec::{encode, decode, LinoValue};
let data = LinoValue::object([
("users", LinoValue::array([
LinoValue::object([("id", LinoValue::Int(1)), ("name", LinoValue::String("Alice".to_string()))]),
LinoValue::object([("id", LinoValue::Int(2)), ("name", LinoValue::String("Bob".to_string()))]),
])),
("metadata", LinoValue::object([
("version", LinoValue::Int(1)),
("count", LinoValue::Int(2)),
])),
]);
assert_eq!(decode(&encode(&data)).unwrap(), data);C#:
var data = new Dictionary<string, object?>
{
{
"users", new List<object?>
{
new Dictionary<string, object?> { { "id", 1 }, { "name", "Alice" } },
new Dictionary<string, object?> { { "id", 2 }, { "name", "Bob" } }
}
},
{ "metadata", new Dictionary<string, object?> { { "version", 1 }, { "count", 2 } } }
};
var decoded = Codec.Decode(Codec.Encode(data));The indented format provides a human-readable representation for displaying objects:
JavaScript:
import { formatIndented, parseIndented } from 'lino-objects-codec';
// Format an object with an identifier
const formatted = formatIndented({
id: '6dcf4c1b-ff3f-482c-95ab-711ea7d1b019',
obj: { uuid: '6dcf4c1b-ff3f-482c-95ab-711ea7d1b019', status: 'executed', command: 'echo test', exitCode: '0' }
});
console.log(formatted);
// Output:
// 6dcf4c1b-ff3f-482c-95ab-711ea7d1b019
// uuid "6dcf4c1b-ff3f-482c-95ab-711ea7d1b019"
// status "executed"
// command "echo test"
// exitCode "0"
// Parse it back
const { id, obj } = parseIndented({ text: formatted });Python:
from link_notation_objects_codec import format_indented, parse_indented
# Format an object with an identifier
formatted = format_indented(
'6dcf4c1b-ff3f-482c-95ab-711ea7d1b019',
{'uuid': '6dcf4c1b-ff3f-482c-95ab-711ea7d1b019', 'status': 'executed'}
)
# Parse it back
id, obj = parse_indented(formatted)Rust:
use lino_objects_codec::format::{format_indented_ordered, parse_indented};
// Format an object with an identifier
let pairs = [("status", "executed"), ("exitCode", "0")];
let formatted = format_indented_ordered("my-uuid", &pairs, " ").unwrap();
// Parse it back
let (id, obj) = parse_indented(&formatted).unwrap();C#:
using Lino.Objects.Codec;
// Format an object with an identifier
var obj = new Dictionary<string, string?> { { "status", "executed" }, { "exitCode", "0" } };
var formatted = Format.FormatIndented("my-uuid", obj);
// Parse it back
var (id, parsedObj) = Format.ParseIndented(formatted);The library uses the links-notation format as the serialization target. Each object is encoded as a Link with type information:
- Basic types are encoded with type markers:
(int 42),(str aGVsbG8=),(bool True) - Strings are base64-encoded to handle special characters and newlines
- Collections with self-references use built-in links notation self-reference syntax:
- Format:
(obj_id: type content...) - Python example:
(obj_0: dict ((str c2VsZg==) obj_0))for{"self": obj} - JavaScript example:
(obj_0: array (int 1) (int 2) obj_0)for self-referencing array
- Format:
- Simple collections without shared references use format:
(list item1 item2 ...)or(dict (key val) ...) - Circular references use direct object ID references:
obj_0(without therefkeyword)
This approach allows for:
- Universal representation of object graphs
- Preservation of object identity
- Natural handling of circular references using built-in links notation syntax
- Cross-language compatibility
cd python
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/ -vcd js
npm install
npm test
npm run examplecd rust
cargo test
cargo run --example basic_usagecd csharp
dotnet build
dotnet test
dotnet run --project examples/BasicUsage.csprojContributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Add tests for your changes
- Ensure all tests pass
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the Unlicense - see the LICENSE file for details.
- GitHub Repository
- Links Notation Specification
- PyPI Package (Python)
- npm Package (JavaScript)
- crates.io Package (Rust)
- NuGet Package (C#)
This project is built on top of the links-notation library.