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.