Data-TOON
view release on metacpan or search on metacpan
# Basic usage
my $data = { name => 'Alice', age => 30, active => 1 };
my $toon = Data::TOON->encode($data);
print $toon;
# Output:
# active: true
# age: 30
# name: Alice
my $decoded = Data::TOON->decode($toon);
# Tabular arrays
my $users = {
users => [
{ id => 1, name => 'Alice', role => 'admin' },
{ id => 2, name => 'Bob', role => 'user' }
]
};
print Data::TOON->encode($users);
lib/Data/TOON.pm view on Meta::CPAN
# Basic usage
my $data = { name => 'Alice', age => 30, active => 1 };
my $toon = Data::TOON->encode($data);
print $toon;
# Output:
# active: true
# age: 30
# name: Alice
my $decoded = Data::TOON->decode($toon);
# Tabular arrays
my $users = {
users => [
{ id => 1, name => 'Alice', role => 'admin' },
{ id => 2, name => 'Bob', role => 'user' }
]
};
print Data::TOON->encode($users);
t/01_encode_decode.t view on Meta::CPAN
# Test 3: Simple object decoding
{
my $toon_text = <<'TOON';
id: 123
name: Alice
active: true
TOON
my $data = Data::TOON->decode($toon_text);
ok($data, 'decode() returns data structure');
is($data->{id}, 123, 'decoded id is correct');
is($data->{name}, 'Alice', 'decoded name is correct');
}
# Test 4: Array decoding
{
my $toon_text = <<'TOON';
users[2]{id,name,role}:
1,Alice,admin
2,Bob,user
TOON
t/03_nested_objects.t view on Meta::CPAN
id => 1,
name => 'Alice',
profile => {
age => 30,
email => 'alice@example.com'
},
active => 1
};
my $encoded = Data::TOON->encode($data);
my $decoded = Data::TOON->decode($encoded);
is($decoded->{id}, 1, 'simple field preserved');
is(ref $decoded->{profile}, 'HASH', 'nested profile is hash');
is($decoded->{profile}->{age}, 30, 'nested age preserved');
is($decoded->{active}, 1, 'field after nested object preserved');
}
done_testing;
t/05_security.t view on Meta::CPAN
}
# Test 10: NaN and Infinity are strings, not numbers
{
my $toon_text = <<'TOON';
value1: NaN
value2: Infinity
TOON
my $data = Data::TOON->decode($toon_text);
is($data->{value1}, 'NaN', 'NaN decoded as string');
is($data->{value2}, 'Infinity', 'Infinity decoded as string');
}
# Test 11: Shell injection-like patterns (data escaping)
{
my $toon_text = 'cmd: "rm -rf /; echo pwned"';
my $data = Data::TOON->decode($toon_text);
is($data->{cmd}, "rm -rf /; echo pwned", 'dangerous string preserved literally');
}
t/05_security.t view on Meta::CPAN
# First row should try to split by comma, not pipe
}
# Test 17: Empty nested objects
{
my $toon_text = <<'TOON';
user:
TOON
my $data = Data::TOON->decode($toon_text);
is(ref $data->{user}, 'HASH', 'empty nested object decoded as hash');
}
# Test 18: Newlines in values
{
my $toon_text = "text: \"line1\\nline2\"";
my $data = Data::TOON->decode($toon_text);
is($data->{text}, "line1\nline2", 'escaped newline in value preserved');
}
t/06_delimiters.t view on Meta::CPAN
{
my $data = {
records => [
{ field1 => 'value1', field2 => 'value2' },
{ field1 => 'value3', field2 => 'value4' }
]
};
my $encoder = Data::TOON::Encoder->new(delimiter => "\t");
my $encoded = $encoder->encode($data);
my $decoded = Data::TOON->decode($encoded);
is_deeply($decoded, $data, 'tab round-trip preserves data');
}
# Test 6: Pipe delimiter round-trip
{
my $data = {
records => [
{ field1 => 'value1', field2 => 'value2' },
{ field1 => 'value3', field2 => 'value4' }
]
};
my $encoder = Data::TOON::Encoder->new(delimiter => '|');
my $encoded = $encoder->encode($data);
my $decoded = Data::TOON->decode($encoded);
is_deeply($decoded, $data, 'pipe round-trip preserves data');
}
# Test 7: Delimiter with comma in values (should be quoted)
{
my $data = {
items => [
{ desc => 'hello,world', id => 1 },
{ desc => 'foo,bar', id => 2 }
]
};
my $encoder = Data::TOON::Encoder->new(delimiter => '|');
my $encoded = $encoder->encode($data);
my $decoded = Data::TOON->decode($encoded);
is($decoded->{items}->[0]->{desc}, 'hello,world', 'comma in value with pipe delimiter');
}
done_testing;
t/07_root_forms.t view on Meta::CPAN
{
my $toon_text = 'data: 1e3';
my $data = Data::TOON->decode($toon_text);
is($data->{data}, 1000, 'canonical: scientific notation');
}
# Test 16: Round-trip root primitive
{
for my $value (42, 3.14, 'hello', 1, 0) {
my $encoded = Data::TOON->encode($value);
my $decoded = Data::TOON->decode($encoded);
is($decoded, $value, "round-trip: $value");
}
}
# Test 17: Round-trip root array
{
my $data = [1, 2, 3];
my $encoded = Data::TOON->encode($data);
my $decoded = Data::TOON->decode($encoded);
is_deeply($decoded, $data, 'round-trip root array');
}
done_testing;
t/09_nested_hash_from_json.t view on Meta::CPAN
my $json = '{"items":[{"id":1,"meta":{"tags":["a","b"],"info":"test"}}]}';
my $data = JSON::PP::decode_json($json);
my $toon = Data::TOON->encode($data);
ok($toon, 'encode() handles arrays with nested hashes from JSON');
unlike($toon, qr/HASH\(0x/, 'nested hashes in array elements are expanded');
like($toon, qr/meta/, 'meta key is present');
like($toon, qr/info/, 'info key is present');
}
# Test 4: Compare with manually created structure vs JSON decoded
{
# Manually created structure
my $manual = {
result => {
tools => [
{
name => 'test',
inputSchema => {
type => 'object',
properties => {}
}
}
]
}
};
# JSON decoded structure (should behave the same)
my $json = '{"result":{"tools":[{"name":"test","inputSchema":{"type":"object","properties":{}}}]}}';
my $from_json = JSON::PP::decode_json($json);
my $toon_manual = Data::TOON->encode($manual);
my $toon_json = Data::TOON->encode($from_json);
# Both should not contain HASH references
unlike($toon_manual, qr/HASH\(0x/, 'manually created structure is properly expanded');
unlike($toon_json, qr/HASH\(0x/, 'JSON decoded structure is properly expanded');
# Both should produce similar output (order may differ due to hash randomization)
like($toon_manual, qr/inputSchema/, 'manual: contains inputSchema');
like($toon_json, qr/inputSchema/, 'json: contains inputSchema');
}
done_testing;
( run in 0.457 second using v1.01-cache-2.11-cpan-9383018d099 )