Skip to content

link-foundation/lino-objects-codec

Repository files navigation

lino-objects-codec

JS CI Python CI Rust CI C# CI

Package versions

npm PyPI crates.io NuGet Python Version

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.

🌍 Multi-Language Support

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.

Features

  • 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: LinoValue enum with Null, 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
  • 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() and decode() 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

Quick Start

Python

pip install lino-objects-codec
from 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 == data

JavaScript

npm install lino-objects-codec
import { 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

Rust

[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);

C#

dotnet add package Lino.Objects.Codec
using 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

Repository Structure

.
├── 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

Language-Specific Documentation

For detailed documentation, API reference, and examples, see:

Usage Examples

All implementations support the same features with language-appropriate syntax:

Circular References

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 preserved

JavaScript:

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 preserved

Rust:

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/decoding

C#:

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 preserved

Complex Nested Structures

Python:

data = {
    "users": [
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"}
    ],
    "metadata": {"version": 1, "count": 2}
}
assert decode(encode(data)) == data

JavaScript:

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));

Indented Links Notation Format

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);

How It Works

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
  • Simple collections without shared references use format: (list item1 item2 ...) or (dict (key val) ...)
  • Circular references use direct object ID references: obj_0 (without the ref keyword)

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

Development

Python

cd python
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/ -v

JavaScript

cd js
npm install
npm test
npm run example

Rust

cd rust
cargo test
cargo run --example basic_usage

C#

cd csharp
dotnet build
dotnet test
dotnet run --project examples/BasicUsage.csproj

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Add tests for your changes
  4. Ensure all tests pass
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

License

This project is licensed under the Unlicense - see the LICENSE file for details.

Links

Acknowledgments

This project is built on top of the links-notation library.

About

A library to encode/decode objects to/from links notation

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors