Code review: JSON unmarshaller

Jacob Carlborg doob at me.com
Mon Oct 15 23:37:54 PDT 2012


On 2012-10-15 22:35, Tyler Jameson Little wrote:

> I'm basically trying to reproduce other JSON marshallers, like Go's, but
> using compile-time reflection. Go uses runtime reflection, which D
> notably does not support. I like the idea of compile-time reflection
> better anyway. There are a few things that would make it easier (like a
> __traits call like allMembers that excludes functions).

Most other languages are not as complicated as D, it's basically only C 
and C++ that are. Implementing a marshaller in Ruby would be dead 
simple. No pointers, no array slices (in the same way as D), support for 
full runtime reflection.

> I use a lot of JSON, so a JSON marshaller/unmarshaller is going to save
> a lot of time, and make my code a lot cleaner.

Most of these points are when unmarshalling. I haven't actually looked 
if your marshaller can handle these cases but looking at the small 
amount of code I would guess no.

>> * Pointers
>
> I've done this, but haven't fully tested it. Basic pointers work.

Are they correctly setup when unmarshaling. Example:

int a = 3; // global/TLS

class Foo
{
     int b = 4;
     int* c;
     int* d;
}

auto foo = new Foo;
foo.c = &a;
foo.d = &foo.b;

When unmarshaling will "foo.d" point to "foo.b"?

>> * Array slices
>
> I think this is handled.

This is basically the same as pointers:

class Foo
{
     int[] a;
     int[] b;
}

auto foo = new Foo;
foo.a = [3, 4, 5, 6];
foo.b = foo.a[1 .. 3];

When unmarshaling will "foo.b" point to "foo.a"?

>> * Serializing through base class references
>
> Doesn't __traits(allMembers, T) give everything from all super classes?

__traits only work at compile time.

class A
{
     int a;
}

class B : A
{
     int b;
}

A b = new B;

The static type of "b" is "A" so all information about "B" is lost at 
compile time. You either need to provide a way to register all 
subclasses that should be be marshaled through a base class reference or 
you need to implement proper runtime reflection.

>> * const/immutable fields
>
> Hmm, not sure to handle this. These have to be set in the constructor,
> right?

You shouldn't call the constructor when unmarshaling. That's another 
problem. Do you want to limit your marshaller to only work with classes 
that have a default constructor or none.

You need to create the class instances without calling the constructor. 
Then you could provide a method that will be called before/after 
unmarshaling.

Have a look that this post:

http://www.digitalmars.com/d/archives/digitalmars/D/Deserializing_const_fields_175774.html

>> * Any reference type (not really hard but it's more work)
>
> Are you talking about aliases? What other kind of reference types are
> there in structs/classes? I'm assuming this will have more to do with
> marshalling as opposed to unmarshalling.

Yes, you don't want to marshal the same object twice. References types 
in D are: objects, pointers, associative arrays and arrays. These are 
the ones I can think of for now.

>> Have a look at for a basically fully working serialization library
>> Orange:
>>
>> https://github.com/jacob-carlborg/orange
>
> Hmm, looks interesting. This looks like it only supports XML, which I
> don't use, but I'm sure you've already solved a lot of the corner cases.
> Thanks, I'll take a look!

I have solved a lot of corner cases but there are a few left. I have a 
branch for handling const/immutable fields but it needs more testing 
before merging it with the master branch.

I'm also not really happy about the deserializing of arrays. It's quite 
slow.

Apparently it's also breaks as soon as you turn on some kind of 
optimization when compiling.

The goal of Orange was to be able serialize basically everything found 
in D. Also to support multiple archive types, i.e. XML, JSON, binary and 
so on.

-- 
/Jacob Carlborg


More information about the Digitalmars-d-learn mailing list