Report a bug
If you spot a problem with this page, click here to create a GitHub issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page.
Requires a signed-in GitHub account. This works well for small changes.
If you'd like to make larger changes you may want to consider using
a local clone.
mir.deser.text
Ion Text Deserialization API
Heavily influenced (and compatible) with upstream Ion implementations (compatible with ion-go)
Authors:
Harrison Ford
- struct
IonTextDeserializer
(Serializer); - Deserializer for the Ion Text format
- pure @safe this(Serializer*
ser
); - ConstructorParameters:
Serializer* ser
A pointer to a serializer - pure @safe void
opCall
(scope const(char)[]text
); - This function starts the deserializing process, and attempts to fully read through the text provided until it reaches the end.Parameters:
const(char)[] text
The text to deserialize
- T
deserializeText
(T)(scope const(char)[]text
);
voiddeserializeText
(T)(ref Tvalue
, scope const(char)[]text
); - Deserialize an Ion Text value to a D value.Parameters:
T value
(optional) value to deserialize const(char)[] text
The text to deserialize Returns:The deserialized Ion Text valueExamples:Test struct deserializationimport mir.ion.value; static struct Book { string title; bool wouldRecommend; string description; uint numberOfNovellas; double price; float weight; string[] tags; } static immutable textData = ` { "title": "A Hero of Our Time", "wouldRecommend": true, "description": "", "numberOfNovellas": 5, "price": 7.99, "weight": 6.88, "tags": ["russian", "novel", "19th century"] }`; Book book = deserializeText!Book(textData); assert(book.description.length == 0); assert(book.numberOfNovellas == 5); assert(book.price == 7.99); assert(book.tags.length == 3); assert(book.tags[0] == "russian"); assert(book.tags[1] == "novel"); assert(book.tags[2] == "19th century"); assert(book.title == "A Hero of Our Time"); assert(book.weight == 6.88f); assert(book.wouldRecommend);
Examples:Test @nogc struct deserializationimport mir.ion.value; import mir.bignum.decimal; import mir.small_string; import mir.small_array; import mir.conv : to; static struct Book { SmallString!64 title; bool wouldRecommend; SmallString!64 description; uint numberOfNovellas; Decimal!1 price; double weight; SmallArray!(SmallString!(16), 10) tags; } static immutable textData = ` { "title": "A Hero of Our Time", "wouldRecommend": true, "description": "", "numberOfNovellas": 5, "price": 7.99, "weight": 6.88, "tags": ["russian", "novel", "19th century"] }`; Book book = deserializeText!Book(textData); assert(book.description.length == 0); assert(book.numberOfNovellas == 5); assert(book.price.to!double == 7.99); assert(book.tags.length == 3); assert(book.tags[0] == "russian"); assert(book.tags[1] == "novel"); assert(book.tags[2] == "19th century"); assert(book.title == "A Hero of Our Time"); assert(book.weight == 6.88f); assert(book.wouldRecommend);
Examples:Test that strings are being de-serialized properlyimport mir.ion.stream; import mir.ion.conv : text2ion; import mir.ser.text; void test(const(char)[] ionData, const(char)[] expected) { const(char)[] output = ionData.text2ion.IonValueStream.serializeText; assert(output == expected); } test(`"hello"`, `"hello"`); test(`"hello\x20world"`, `"hello world"`); test(`"hello\u2248world"`, `"hello≈world"`); test(`"hello\U0001F44Dworld"`, `"hello👍world"`);
Examples:Test that timestamps are de-serialized properlyimport mir.ion.stream; import mir.ion.conv : text2ion; import mir.ion.value : IonTimestamp; import std.datetime.date : TimeOfDay; import mir.timestamp : Timestamp; void test(const(char)[] ionData, Timestamp expected) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { Timestamp t = ionValue.get!(IonTimestamp).get; assert(expected == t); } } void testFail(const(char)[] ionData, Timestamp expected) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { Timestamp t = ionValue.get!(IonTimestamp).get; assert(expected != t); } } test("2001-01T", Timestamp(2001, 1)); test("2001-01-02", Timestamp(2001, 1, 2)); test("2001-01-02T", Timestamp(2001, 1, 2)); test("2001-01-02T03:04", Timestamp(2001, 1, 2, 3, 4)); test("2001-01-02T03:04Z", Timestamp(2001, 1, 2, 3, 4)); test("2001-01-02T03:04+00:00", Timestamp(2001, 1, 2, 3, 4)); test("2001-01-02T03:05+00:01", Timestamp(2001, 1, 2, 3, 4).withOffset(1)); test("2001-01-02T05:05+02:01", Timestamp(2001, 1, 2, 3, 4).withOffset(2*60+1)); test("2001-01-02T03:04:05", Timestamp(2001, 1, 2, 3, 4, 5)); test("2001-01-02T03:04:05Z", Timestamp(2001, 1, 2, 3, 4, 5)); test("2001-01-02T03:04:05+00:00", Timestamp(2001, 1, 2, 3, 4, 5)); test("2001-01-02T03:05:05+00:01", Timestamp(2001, 1, 2, 3, 4, 5).withOffset(1)); test("2001-01-02T05:05:05+02:01", Timestamp(2001, 1, 2, 3, 4, 5).withOffset(2*60+1)); test("2001-01-02T03:04:05.666", Timestamp(2001, 1, 2, 3, 4, 5, -3, 666)); test("2001-01-02T03:04:05.666Z", Timestamp(2001, 1, 2, 3, 4, 5, -3, 666)); test("2001-01-02T03:04:05.666666Z", Timestamp(2001, 1, 2, 3, 4, 5, -6, 666_666)); test("2001-01-02T03:54:05.666+00:50", Timestamp(2001, 1, 2, 3, 4, 5, -3, 666).withOffset(50)); test("2001-01-02T03:54:05.666666+00:50", Timestamp(2001, 1, 2, 3, 4, 5, -6, 666_666).withOffset(50)); // Time of day tests test("03:04", Timestamp(0, 0, 0, 3, 4)); test("03:04Z", Timestamp(0, 0, 0, 3, 4)); test("03:04+00:00", Timestamp(0, 0, 0, 3, 4)); test("03:05+00:01", Timestamp(0, 0, 0, 3, 4).withOffset(1)); test("05:05+02:01", Timestamp(0, 0, 0, 3, 4).withOffset(2*60+1)); test("03:04:05", Timestamp(0, 0, 0, 3, 4, 5)); test("03:04:05Z", Timestamp(0, 0, 0, 3, 4, 5)); test("03:04:05+00:00", Timestamp(0, 0, 0, 3, 4, 5)); test("03:05:05+00:01", Timestamp(0, 0, 0, 3, 4, 5).withOffset(1)); test("05:05:05+02:01", Timestamp(0, 0, 0, 3, 4, 5).withOffset(2*60+1)); test("03:04:05.666", Timestamp(0, 0, 0, 3, 4, 5, -3, 666)); test("03:04:05.666Z", Timestamp(0, 0, 0, 3, 4, 5, -3, 666)); test("03:04:05.666666Z", Timestamp(0, 0, 0, 3, 4, 5, -6, 666_666)); test("03:54:05.666+00:50", Timestamp(0, 0, 0, 3, 4, 5, -3, 666).withOffset(50)); test("03:54:05.666666+00:50", Timestamp(0, 0, 0, 3, 4, 5, -6, 666_666).withOffset(50)); // Mir doesn't like 03:04 only (as technically it's less precise then TimeOfDay)... ugh test("03:04:05", Timestamp(TimeOfDay(3, 4, 5))); test("03:04:05Z", Timestamp(TimeOfDay(3, 4, 5))); test("03:04:05+00:00", Timestamp(TimeOfDay(3, 4, 5))); test("03:05:05+00:01", Timestamp(TimeOfDay(3, 4, 5)).withOffset(1)); test("05:05:05+02:01", Timestamp(TimeOfDay(3, 4, 5)).withOffset(2*60+1)); testFail("2001-01-02T03:04+00:50", Timestamp(2001, 1, 2, 3, 4)); testFail("2001-01-02T03:04:05+00:50", Timestamp(2001, 1, 2, 3, 4, 5)); testFail("2001-01-02T03:04:05.666Z", Timestamp(2001, 1, 2, 3, 4, 5)); testFail("2001-01-02T03:54:05.666+00:50", Timestamp(2001, 1, 2, 3, 4, 5)); // Fake timestamps for Duration encoding import core.time : weeks, days, hours, minutes, seconds, hnsecs; test("0005-02-88T07:40:04.9876543", Timestamp(5.weeks + 2.days + 7.hours + 40.minutes + 4.seconds + 9876543.hnsecs)); test("0005-02-99T07:40:04.9876543", Timestamp(-5.weeks - 2.days - 7.hours - 40.minutes - 4.seconds - 9876543.hnsecs));
Examples:Test that binary literals are de-serialized properly.import mir.ion.value : IonUInt; import mir.ion.stream; import mir.ion.conv : text2ion; void test(const(char)[] ionData, uint val) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(IonUInt); assert(v.get!uint == val); } } test("0b00001", 0b1); test("0b10101", 0b10101); test("0b11111", 0b11111); test("0b111111111111111111111", 0b1111_1111_1111_1111_1111_1); test("0b1_1111_1111_1111_1111_1111", 0b1_1111_1111_1111_1111_1111);
Examples:Test that signed / unsigned integers are de-serialized properly.import mir.ion.value : IonUInt, IonNInt; import mir.ion.stream; import mir.ion.conv : text2ion; void test(const(char)[] ionData, ulong val) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(IonUInt); assert(v.get!ulong == val); } } void testNeg(const(char)[] ionData, ulong val) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(IonNInt); assert(v.get!long == -val); } } test("0xabc_def", 0xabc_def); test("0xabcdef", 0xabcdef); test("0xDEADBEEF", 0xDEADBEEF); test("0xDEADBEEF", 0xDEAD_BEEF); test("0xDEAD_BEEF", 0xDEAD_BEEF); test("0xDEAD_BEEF", 0xDEADBEEF); test("0x0123456789", 0x0123456789); test("0x0123456789abcdef", 0x0123456789abcdef); test("0x0123_4567_89ab_cdef", 0x0123_4567_89ab_cdef); testNeg("-0xabc_def", 0xabc_def); testNeg("-0xabc_def", 0xabc_def); testNeg("-0xabcdef", 0xabcdef); testNeg("-0xDEADBEEF", 0xDEADBEEF); testNeg("-0xDEADBEEF", 0xDEAD_BEEF); testNeg("-0xDEAD_BEEF", 0xDEAD_BEEF); testNeg("-0xDEAD_BEEF", 0xDEADBEEF); testNeg("-0x0123456789", 0x0123456789); testNeg("-0x0123456789abcdef", 0x0123456789abcdef); testNeg("-0x0123_4567_89ab_cdef", 0x0123_4567_89ab_cdef);
Examples:Test that infinity & negative infinity are deserialized properly.import mir.ion.value : IonFloat; import mir.ion.conv : text2ion; import mir.ion.stream; void test(const(char)[] ionData, float expected) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(IonFloat); assert(v.get!float == expected); } } test("-inf", -float.infinity); test("+inf", float.infinity);
Examples:Test that NaN is deserialized properly.import mir.ion.value; import mir.ion.conv : text2ion; import mir.ion.stream; alias isNaN = x => x != x; void test(const(char)[] ionData) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(IonFloat); assert(isNaN(v.get!float)); } } test("nan");
Examples:Test that signed / unsigned integers and decimals and floats are all deserialized properly.import mir.ion.value; import mir.ion.stream; import mir.ion.conv : text2ion; void test_uint(const(char)[] ionData, ulong expected) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(IonUInt); assert(v.get!ulong == expected); } } void test_nint(const(char)[] ionData, long expected) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(IonNInt); assert(v.get!long == expected); } } void test_dec(const(char)[] ionData, double expected) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(IonDecimal); assert(v.get!double == expected); } } void test_float(const(char)[] ionData, float expected) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(IonFloat); assert(v.get!float == expected); } } test_uint("123", 123); test_nint("-123", -123); test_dec("123.123123", 123.123123); test_dec("123.123123", 123.123123); test_dec("123.123123d0", 123.123123); test_dec("123.123123d0", 123.123123); test_dec("-123.123123", -123.123123); test_dec("-123.123123d0", -123.123123); test_dec("18446744073709551615.", 1844_6744_0737_0955_1615.0); test_dec("-18446744073709551615.", -1844_6744_0737_0955_1615.0); test_dec("18446744073709551616.", 1844_6744_0737_0955_1616.0); test_dec("-18446744073709551616.", -1844_6744_0737_0955_1616.0); test_float("123.456789e-6", 123.456789e-6); test_float("-123.456789e-6", -123.456789e-6);
Examples:Test that quoted / unquoted symbols are deserialized properly.import mir.ion.value; import mir.ion.conv : text2ion; import mir.ion.stream; void test(const(char)[] ionData, string symbol) { foreach (symbolTable, val; ionData.text2ion.IonValueStream) { auto sym = val.get!(IonSymbolID).get; assert(symbol == symbolTable[sym]); } } test("$0", "$0"); test("$ion", "$ion"); test("$ion_1_0", "$ion_1_0"); test("name", "name"); test("version", "version"); test("imports", "imports"); test("symbols", "symbols"); test("max_id", "max_id"); test("$ion_shared_symbol_table", "$ion_shared_symbol_table"); test("hello", "hello"); test("world", "world"); test("'foobaz'", "foobaz"); test("'👍'", "👍"); test("' '", " "); test("'\\U0001F44D'", "👍"); test("'\\u2248'", "\u2248"); test("'true'", "true"); test("'false'", "false"); test("'nan'", "nan"); test("'null'", "null");
Examples:Test that all variations of the "null" value are deserialized properly.import mir.ion.value; import mir.ion.stream; import mir.ion.conv : text2ion; void test(const(char)[] ionData, IonTypeCode nullType) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(IonNull); assert(v.code == nullType); } } test("null", IonTypeCode.null_); test("null.bool", IonTypeCode.bool_); test("null.int", IonTypeCode.uInt); test("null.float", IonTypeCode.float_); test("null.decimal", IonTypeCode.decimal); test("null.timestamp", IonTypeCode.timestamp); test("null.symbol", IonTypeCode.symbol); test("null.string", IonTypeCode.string); test("null.blob", IonTypeCode.blob); test("null.clob", IonTypeCode.clob); test("null.list", IonTypeCode.list); test("null.struct", IonTypeCode.struct_); test("null.sexp", IonTypeCode.sexp);
Examples:Test that blobs are getting de-serialized correctly.import mir.ion.value; import mir.ion.stream; import mir.ion.conv : text2ion; import mir.lob; void test(const(char)[] ionData, ubyte[] blobData) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(Blob); assert(v.data == blobData); } } test("{{ SGVsbG8sIHdvcmxkIQ== }}", cast(ubyte[])"Hello, world!"); test("{{ R29vZCBhZnRlcm5vb24hIPCfkY0= }}", cast(ubyte[])"Good afternoon! 👍");
Examples:Test that long/short clobs are getting de-serialized correctly.import mir.ion.value; import mir.ion.stream; import mir.ion.conv : text2ion; import mir.lob; void test(const(char)[] ionData, const(char)[] blobData) { foreach(symbolTable, ionValue; ionData.text2ion.IonValueStream) { auto v = ionValue.get!(Clob); assert(v.data == blobData); } } test(`{{ "This is a short clob." }}`, "This is a short clob."); test(` {{ '''This is a long clob,''' ''' which spans over multiple lines,''' ''' and can have a theoretically infinite length.''' }}`, "This is a long clob, which spans over multiple lines, and can have a theoretically infinite length."); test(`{{ '''Long clobs can also have their data contained in one value, but spread out across multiple lines.''' }}`, "Long clobs can also have their data contained in one value,\n but spread out across multiple lines."); test(`{{ '''Or, you can have multiple values on the same line,''' ''' like this!'''}}`, "Or, you can have multiple values on the same line, like this!");
Examples:Test that structs are getting de-serialized properlyimport mir.ion.stream; import mir.ion.conv : text2ion; import mir.ser.text; void test(const(char)[] ionData, const(char)[] expected) { auto v = ionData.text2ion.IonValueStream.serializeText; assert(v == expected); } test(`{"test":"world", test: false, 'test': usd::123.456, '''test''': "asdf"}`, `{test:"world",test:false,test:usd::123.456,test:"asdf"}`); test(`{'''foo''' '''bar''': "foobar"}`, `{foobar:"foobar"}`); test(`{a: 1, b: 2}`, `{a:1,b:2}`); test(`{}`, `{}`);
Examples:Test that sexps are getting de-serialized properly.import mir.ion.stream; import mir.ion.conv : text2ion; import mir.ser.text; void test(const(char)[] ionData, const(char)[] expected) { auto v = ionData.text2ion.IonValueStream.serializeText; assert(v == expected); } test(`(this is a sexp list)`, "(this is a sexp list)"); test(`('+' '++' '+-+' '-++' '-' '--' '---' -3 - 3 '--' 3 '--'3 )`, "('+' '++' '+-+' '-++' '-' '--' '---' -3 '-' 3 '--' 3 '--' 3)"); test(`(a_plus_plus_plus_operator::+++ a_3::3)`, `(a_plus_plus_plus_operator::'+++' a_3::3)`); test(`(& (% -[42, 3]+(2)-))`, `('&' ('%' '-' [42,3] '+' (2) '-'))`);
Examples:Test that arrays are getting de-serialized properly.import mir.ion.stream; import mir.ion.conv : text2ion; import mir.ser.text; void test(const(char)[] ionData, const(char)[] expected) { auto v = ionData.text2ion.IonValueStream.serializeText; assert(v == expected); } test(`[hello, world]`, `[hello,world]`); test(`[this::is::an::annotated::symbol, this::is::annotated::123.456]`, `[this::is::an::annotated::symbol,this::is::annotated::123.456]`); test(`[date::of::birth::0001-01-01T00:00:00.0-00:00, date::of::birth::1970-01-01T]`, `[date::of::birth::0001-01-01T00:00:00.0Z,date::of::birth::1970-01-01]`); test(`['hello', "hello", '''hello''', '''hello ''''''world''']`, `[hello,"hello","hello","hello world"]`); test(`[0x123_456, 0xF00D_BAD]`, `[1193046,251714477]`);
Examples:Test that annotations work with symbolsimport mir.ion.stream; import mir.ser.text; import mir.ion.conv : text2ion; void test(const(char)[] ionData, const(char)[] expected) { auto v = ionData.text2ion.IonValueStream.serializeText; assert(v == expected); } test(`'test'::'hello'::'world'`, "test::hello::world"); test(`foo::bar`, "foo::bar"); test(`foo::'bar'`, "foo::bar"); test(`'foo'::bar`, "foo::bar"); test(`'foo bar'::cash`, "'foo bar'::cash"); test(`'foo\U0001F44D'::'baz\U0001F44D'`, "'foo\U0001F44D'::'baz\U0001F44D'"); test(`'\u2248'::'\u2248'`, "'\u2248'::'\u2248'"); test(`'\u2248'::foo`, "'\u2248'::foo");
Examples:Test that annotations work with floatsimport mir.ion.stream; import mir.ion.conv : text2ion; import mir.ser.text; void test(const(char)[] ionData, const(char)[] expected) { auto v = ionData.text2ion.IonValueStream.serializeText; assert(v == expected); } test(`usd::10.50e0`, "usd::10.5"); test(`'Value is good \U0001F44D'::12.34e0`, "'Value is good \U0001F44D'::12.34"); test(`'null'::150.00e0`, "'null'::150.0");
Examples:Test that annotations work with decimalsimport mir.ion.stream; import mir.ion.conv : text2ion; import mir.ser.text; void test(const(char)[] ionData, const(char)[] expected) { auto v = ionData.text2ion.IonValueStream.serializeText; assert(v == expected); } test(`Types::Speed::MetersPerSecondSquared::9.81`, "Types::Speed::MetersPerSecondSquared::9.81"); test(`Rate::USD::GBP::12.345`, "Rate::USD::GBP::12.345"); test(`usd::10.50d0`, "usd::10.50"); test(`'Value is good \U0001F44D'::12.34d0`, "'Value is good \U0001F44D'::12.34"); test(`'null'::150.00d0`, "'null'::150.00"); test(`'Cool'::27182818284590450000000000d-25`, "Cool::2.7182818284590450000000000"); test(`mass::2.718281828459045d0`, "mass::2.718281828459045"); test(`weight::0.000000027182818284590450000000000d+8`, "weight::2.7182818284590450000000000"); test(`coeff::-0.000000027182818284590450000000000d+8`, "coeff::-2.7182818284590450000000000");
Examples:Test that annotations work with stringsimport mir.ion.stream; import mir.ion.conv : text2ion; import mir.ser.text; void test(const(char)[] ionData, const(char)[] expected) { auto v = ionData.text2ion.IonValueStream.serializeText; assert(v == expected); } test(`Password::"Super Secure Password"`, `Password::"Super Secure Password"`); test(`Magic::String::"Hello, world!"`, `Magic::String::"Hello, world!"`); test(`SSH::PublicKey::'''ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNrMk7QmmmNIusf10CwHQHs6Z9HJIiuknwoqtQLzEPxdMnNHKJexNnfF5QQ2v84BBhVjxvPgSqhdcVMEFy8PrGu44MqhK/cV6BGx430v2FnArWDO+9LUSd+3iwMJVZUQgZGtjSLAkZO+NOSPWZ+W0SODGgUfbNVu35GjVoA2+e1lOINUe22oZPnaD+gpJGUOx7j5JqpCblBZntvZyOjTPl3pc52rIGfxi1TYJnDXjqX76OinZceBzp5Oh0oUTrPbu55ig+b8bd4HtzLWxcqXBCnsw0OAKsAiXfLlBcrgZUsoAP9unrcqsqoJ2qEEumdsPqcpJakpO7/n0lMP6lRdSZ'''`, `SSH::PublicKey::"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNrMk7QmmmNIusf10CwHQHs6Z9HJIiuknwoqtQLzEPxdMnNHKJexNnfF5QQ2v84BBhVjxvPgSqhdcVMEFy8PrGu44MqhK/cV6BGx430v2FnArWDO+9LUSd+3iwMJVZUQgZGtjSLAkZO+NOSPWZ+W0SODGgUfbNVu35GjVoA2+e1lOINUe22oZPnaD+gpJGUOx7j5JqpCblBZntvZyOjTPl3pc52rIGfxi1TYJnDXjqX76OinZceBzp5Oh0oUTrPbu55ig+b8bd4HtzLWxcqXBCnsw0OAKsAiXfLlBcrgZUsoAP9unrcqsqoJ2qEEumdsPqcpJakpO7/n0lMP6lRdSZ"`);
Copyright © 2016-2022 by Ilya Yaroshenko | Page generated by
Ddoc on Thu Mar 10 07:43:21 2022