www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - std.json dynamic initialization of JSONValue

reply Kai Meyer <kai unixlords.com> writes:
I posted this on D.learn, but got no responses. I'm hoping it's because 
I'm asking the wrong crowd.

I'm finding std.json extremely well written, with one glaring exception.

I can't seem to figure out how to do this:

JSONValue root = JSONValue(null, JSON_TYPE.OBJECT);
root.object["first_object"] = JSONValue(null, JSON_TYPE.OBJECT);
root.object["first_string"] = JSONValue("first_string", JSON_TYPE.STRING);

which would decode to:

{"first_object":{},"first_string":"first_string"}

What I end up having to do is:
JSONValue root;
root.type = JSON_TYPE.OBJECT;
root.object["first_object"] = JSONValue();
root.object["first_object"].type = JSON_TYPE.OBJECT;
root.object["first_string"] = JSON_Value();
root.object["first_string"].type = JSON_TYPE.STRING;
root.object["first_string"].str = "first_string";

That just feels like I'm doing it wrong. Is there a way to dynamically 
initialize a JSONValue struct? If I try to intialize the JSONValue 
object with anything other than simply null, or empty string, I either 
get a compile error or a segfault at run-time.

root.object["first_object"] = JSONValue(null, JSON_TYPE.OBJECT);

compile error:
Error: overlapping initialization for integer

root.object["first_string"] = JSONValue("first_string");
run-time segfault.

Any ideas?
Dec 06 2011
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
I could swear I replied in the other group... must have gotten lost in the
series of tubes.

Anyway, I think std.json is easiest to use with a little template wrapper
instead
of making JSONValues directly.

Try this on for size:

=====

import std.json;
import std.conv;
import std.traits;

JSONValue toJsonValue(T)(T a) {
	JSONValue val;
	static if(is(T == typeof(null))) {
		val.type = JSON_TYPE.NULL;
	} else static if(is(T == JSONValue)) {
		val = a;
	} else static if(isIntegral!(T)) {
		val.type = JSON_TYPE.INTEGER;
		val.integer = to!long(a);
	} else static if(isFloatingPoint!(T)) {
		val.type = JSON_TYPE.FLOAT;
		val.floating = to!real(a);
	} else static if(is(T == bool)) {
		if(a == true)
			val.type = JSON_TYPE.TRUE;
		if(a == false)
			val.type = JSON_TYPE.FALSE;
	} else static if(isSomeString!(T)) {
		val.type = JSON_TYPE.STRING;
		val.str = to!string(a);
	} else static if(isAssociativeArray!(T)) {
		val.type = JSON_TYPE.OBJECT;
		foreach(k, v; a) {
			val.object[to!string(k)] = toJsonValue(v);
		}
	} else static if(isArray!(T)) {
		val.type = JSON_TYPE.ARRAY;
		val.array.length = a.length;
		foreach(i, v; a) {
			val.array[i] = toJsonValue(v);
		}
	} else static if(is(T == struct)) { // also can do all members of a struct...
		val.type = JSON_TYPE.OBJECT;

		foreach(i, member; a.tupleof) {
			string name = a.tupleof[i].stringof[2..$];
			static if(a.tupleof[i].stringof[2] != '_')
				val.object[name] = toJsonValue!(typeof(member), R)(member,
formatToStringAs, api);
		}
	} else { /* our catch all is to just do strings */
		val.type = JSON_TYPE.STRING;
		val.str = to!string(a);
	}

	return val;
}

string toJson(T)(T a) {
	auto v = toJsonValue(a);
	return toJSON(&v);
}

/* usage example */
import std.stdio;
void main() {
	writeln(toJson(["message": "Hello, world!"]));
}
==========


Then, you can either use toJsonValue() to fill in an object similar
to what you were doing before, or use toJson() to skip right
to having a string from a struct, an associative array, or many
other D types.
Dec 06 2011
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
Adam D. Ruppe Wrote:
 				val.object[name] = toJsonValue!(typeof(member), R)(member,
formatToStringAs, api);

Whoops, that should have been val.object[name] = toJsonValue(member); (I copy pasted this out of my web.d module, which can add a toString() method to the json too, so it's a little more complicated. And probably useless.)
Dec 06 2011
prev sibling parent reply Kai Meyer <kai unixlords.com> writes:
On 12/06/2011 02:42 PM, Adam D. Ruppe wrote:
 I could swear I replied in the other group... must have gotten lost in the
 series of tubes.

 Anyway, I think std.json is easiest to use with a little template wrapper
instead
 of making JSONValues directly.

 Try this on for size:

 =====

 import std.json;
 import std.conv;
 import std.traits;

 JSONValue toJsonValue(T)(T a) {
 	JSONValue val;
 	static if(is(T == typeof(null))) {
 		val.type = JSON_TYPE.NULL;
 	} else static if(is(T == JSONValue)) {
 		val = a;
 	} else static if(isIntegral!(T)) {
 		val.type = JSON_TYPE.INTEGER;
 		val.integer = to!long(a);
 	} else static if(isFloatingPoint!(T)) {
 		val.type = JSON_TYPE.FLOAT;
 		val.floating = to!real(a);
 	} else static if(is(T == bool)) {
 		if(a == true)
 			val.type = JSON_TYPE.TRUE;
 		if(a == false)
 			val.type = JSON_TYPE.FALSE;
 	} else static if(isSomeString!(T)) {
 		val.type = JSON_TYPE.STRING;
 		val.str = to!string(a);
 	} else static if(isAssociativeArray!(T)) {
 		val.type = JSON_TYPE.OBJECT;
 		foreach(k, v; a) {
 			val.object[to!string(k)] = toJsonValue(v);
 		}
 	} else static if(isArray!(T)) {
 		val.type = JSON_TYPE.ARRAY;
 		val.array.length = a.length;
 		foreach(i, v; a) {
 			val.array[i] = toJsonValue(v);
 		}
 	} else static if(is(T == struct)) { // also can do all members of a struct...
 		val.type = JSON_TYPE.OBJECT;

 		foreach(i, member; a.tupleof) {
 			string name = a.tupleof[i].stringof[2..$];
 			static if(a.tupleof[i].stringof[2] != '_')
 				val.object[name] = toJsonValue!(typeof(member), R)(member,
formatToStringAs, api);
 		}
 	} else { /* our catch all is to just do strings */
 		val.type = JSON_TYPE.STRING;
 		val.str = to!string(a);
 	}

 	return val;
 }

 string toJson(T)(T a) {
 	auto v = toJsonValue(a);
 	return toJSON(&v);
 }

 /* usage example */
 import std.stdio;
 void main() {
 	writeln(toJson(["message": "Hello, world!"]));
 }
 ==========


 Then, you can either use toJsonValue() to fill in an object similar
 to what you were doing before, or use toJson() to skip right
 to having a string from a struct, an associative array, or many
 other D types.

I like it. Any reason something like this doesn't already exist in std.json? -Kai Meyer
Dec 06 2011
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
Kai Meyer Wrote:
 I like it. Any reason something like this doesn't already exist in std.json?

I think it's just that nobody has done updates to std.json for a while, but I don't really know.
Dec 06 2011
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, December 06, 2011 18:26:39 Adam D. Ruppe wrote:
 Kai Meyer Wrote:
 I like it. Any reason something like this doesn't already exist in
 std.json?

I don't really know.

I believe that Robert Jacques has a revised version of std.json that he intends to put up for review, but it relies on changes that he's made to std.variant which also need be reviewed. - Jonathan M Davis
Dec 06 2011
prev sibling parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 06 Dec 2011 19:15:26 -0500, Jonathan M Davis <jmdavisProg gmx.com>
wrote:
 On Tuesday, December 06, 2011 18:26:39 Adam D. Ruppe wrote:
 Kai Meyer Wrote:
 I like it. Any reason something like this doesn't already exist in
 std.json?

I don't really know.

I believe that Robert Jacques has a revised version of std.json that he intends to put up for review, but it relies on changes that he's made to std.variant which also need be reviewed. - Jonathan M Davis

Correct. Andrei suggested that since JSONValue is a tagged union, it should really use std.Variant.Algebraic as its representation, which necessitated fixing both Variant and Algebraic so that was possible. The code and documentation links are below if you'd like to try it out/comment on it. The only thing unintuitive in my revision from your use case is creating an empty JSON object or array: JSON.Value myObject = JSON.Value[string].init; JSON.Value myArray = JSON.Value[].init; Since this is undocumented :( I think I'm going to add some aliases (JSON.Object_init, JSON.Array_init) to simplify things for people. https://jshare.johnshopkins.edu/rjacque2/public_html/json2.mht https://jshare.johnshopkins.edu/rjacque2/public_html/variant.mht https://jshare.johnshopkins.edu/rjacque2/public_html/json2.d https://jshare.johnshopkins.edu/rjacque2/public_html/variant.d
Dec 06 2011
parent Adam D. Ruppe <destructionator gmail.com> writes:
Robert Jacques;

I like your modules a lot. I'm sure it will be a bit of a pain to integrate
it into my existing codebase, but they look like it'd be worth it!
Dec 06 2011
prev sibling parent David <d dav1d.de> writes:
Am 06.12.2011 22:30, schrieb Kai Meyer:
 I posted this on D.learn, but got no responses. I'm hoping it's because
 I'm asking the wrong crowd.

 I'm finding std.json extremely well written, with one glaring exception.

 I can't seem to figure out how to do this:

 JSONValue root = JSONValue(null, JSON_TYPE.OBJECT);
 root.object["first_object"] = JSONValue(null, JSON_TYPE.OBJECT);
 root.object["first_string"] = JSONValue("first_string", JSON_TYPE.STRING);

 which would decode to:

 {"first_object":{},"first_string":"first_string"}

 What I end up having to do is:
 JSONValue root;
 root.type = JSON_TYPE.OBJECT;
 root.object["first_object"] = JSONValue();
 root.object["first_object"].type = JSON_TYPE.OBJECT;
 root.object["first_string"] = JSON_Value();
 root.object["first_string"].type = JSON_TYPE.STRING;
 root.object["first_string"].str = "first_string";

 That just feels like I'm doing it wrong. Is there a way to dynamically
 initialize a JSONValue struct? If I try to intialize the JSONValue
 object with anything other than simply null, or empty string, I either
 get a compile error or a segfault at run-time.

 root.object["first_object"] = JSONValue(null, JSON_TYPE.OBJECT);

 compile error:
 Error: overlapping initialization for integer

 root.object["first_string"] = JSONValue("first_string");
 run-time segfault.

 Any ideas?

me switch, but I didn't switch back): https://256.makerslocal.org/wiki/Libdjson
Dec 07 2011