January 04, 2019
As documented, when you output a JSONValue to string, keys are always in ascent order. In some case the output will be consumed by human so a specific key order is important.

This really caused my headache for some time. When I was about to rewrite my own toString method I found a quick, dirty but clever idea as below.

string toPrettyStringWithOrder(JSONValue j, string[] keys) {
	auto str = j.toString();
	for (int i = 0; i < keys.length; i++) {
		string oldkey = keys[i];
		string newkey = format("__%02d__", i) ~ oldkey;
		str = str.replace("\"" ~ oldkey ~ "\":", "\"" ~ newkey ~ "\":");
	}
	JSONValue jj = parseJSON(str);
	str = jj.toPrettyString();
	
	auto re = regex(r"__[0-9][0-9]__","g");
	return replaceAll(str, re, "");
}

my usage:
f.write(toPrettyStringWithOrder(j, ["name", "group", "type", "talent", "skill", "source", "tag", "gallary"]));

It lacks of preciseness. But it's enough to me. Hope this help you as well.
January 04, 2019
On Friday, 4 January 2019 at 07:38:39 UTC, timepp wrote:
> As documented, when you output a JSONValue to string, keys are always in ascent order. In some case the output will be consumed by human so a specific key order is important.
>
> This really caused my headache for some time. When I was about to rewrite my own toString method I found a quick, dirty but clever idea as below.
>
> string toPrettyStringWithOrder(JSONValue j, string[] keys) {
> 	auto str = j.toString();
> 	for (int i = 0; i < keys.length; i++) {
> 		string oldkey = keys[i];
> 		string newkey = format("__%02d__", i) ~ oldkey;
> 		str = str.replace("\"" ~ oldkey ~ "\":", "\"" ~ newkey ~ "\":");
> 	}
> 	JSONValue jj = parseJSON(str);
> 	str = jj.toPrettyString();
> 	
> 	auto re = regex(r"__[0-9][0-9]__","g");
> 	return replaceAll(str, re, "");
> }
>
> my usage:
> f.write(toPrettyStringWithOrder(j, ["name", "group", "type", "talent", "skill", "source", "tag", "gallary"]));
>
> It lacks of preciseness. But it's enough to me. Hope this help you as well.

About a year ago I had the same problem.  I was able to extend std.json with a "policy" that allowed an application to specify custom types for json objects.  Then in my application I just defined my own object type that preserved the field order that the json object was parsed from.

I created a Pull Request in phobos for std.json to allow applications to specify a policy to customize json behavior (https://github.com/dlang/phobos/pull/6059), however, Andrei rejected the phobos PR saying that preserving field order was an "anitpatern" in JSON (even though the phobos PR was just adding a Policy interface, nothing to do with preserving field order) and also saying that std.json is disliked so adding features doesn't make any sense.  Andrei tends to skim over pull requests without understanding them and its impossible to maintain a conversation with him to explain so I just closed the pull request so I didn't have to deal with it.

In the end, to preserve field order I just implemented my own simple JSON parser in 400 lines of D code: https://github.com/marler8997/stupidjsonparser