Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

merge JSON structures

84 views
Skip to first unread message

hobosa...@gmail.com

unread,
Nov 12, 2006, 11:43:21 PM11/12/06
to
Is there a function or library somewhere that merges 2 JSON data
structures? For instance this:

{ "one": [1,3] }

plus

{ "one": [2] }

equals

{ "one": [1,2,3] }

webEater

unread,
Nov 13, 2006, 12:02:28 AM11/13/06
to

This is a very special case. In prototype library you find
Object.extend(), but it overwrites same keys. You need a special
funcion that concats Arrays key for key (if the key holds an array),
e.g.

Object.plus = function(obj1, obj2) {
var res = {};
// "copy" obj1 keys to new object res
Object.extend(res, obj1);
// now overwrite obj1 keys in res by contents of obj2
Object.extend(res, obj2);
// now consider overwritten obj 1 keys one by one, like in example -
only arrays
for(var key in obj1) {
if (obj1[key] instanceof Array)
// concatenate Arrays, don't sort them like in your example
res[key] = obj1[key].concat(res[key]);
}
}

I hope I could help you, for Object.extend look in
http://script.aculo.us/prototype.js

Andi

webEater

unread,
Nov 13, 2006, 12:05:11 AM11/13/06
to
And return the result:

Object.plus = function(obj1, obj2) {
...
return res;
}

RobG

unread,
Nov 13, 2006, 2:29:49 AM11/13/06
to

If you are specifically after the above structure, then the following
should do the trick:

<script type="text/javascript" src="json.js"></script>
<script type="text/javascript">

var jsonStr1 = '{ "one": [1,3] }';
var jsonStr2 = '{ "one": [2],'
+ ' "two": [1]}';

function jsonAdd(str1, str2){
var obj1 = eval('(' + str1 + ')');
var obj2 = eval('(' + str2 + ')');
for (var prop in obj2){
if (obj2[prop] instanceof Array){
if (prop in obj1){
obj1[prop] = obj1[prop].concat(obj2[prop]);
} else {
obj1[prop] = obj2[prop].concat();
}
}
}
return obj1.toJSONString();
}

alert( jsonAdd(jsonStr1, jsonStr2) );

</script>

where json.js can be found at: <URL: http://www.json.org/json.js >. If
you don't want to extend the Array prototype (and therefore could
remove the instanceof Array test), there is a functionalised version
that is no longer available from json.org - it has been posted here:
<URL:
http://groups.google.com.au/group/rubyonrails-spinoffs/browse_frm/thread/63b46996d66e9372/ef43fb9e0b2f5a3d?lnk=gst&q=Fred&rnum=8#ef43fb9e0b2f5a3d
>


--
Rob

hobosa...@gmail.com

unread,
Nov 13, 2006, 5:04:57 AM11/13/06
to
webEater wrote:
> This is a very special case. In prototype library you find
> Object.extend(), but it overwrites same keys. You need a special
> funcion that concats Arrays key for key (if the key holds an array),

It gets more complicated because { "this":"that" } is an object with no
concat method. So from what I can tell I need to write my own function
that looks at typeof and the constructor property to tell if I have an
array, object, or something else, and either concat, straight assign,
or loop through object properties and assign. Yuck, oh well.

I'm sure someone has done this before, I've never been into javascript,
there isn't anything like CPAN or PEAR for JS is there? All these "free
DHTML scripts!" web sites sure don't cut it.

HS

hobosa...@gmail.com

unread,
Nov 13, 2006, 5:06:43 AM11/13/06
to
RobG wrote:
> If you are specifically after the above structure

Nope, I'm after a big complicated structure that'll probably get bigger
and more complicated later. But thanks!

> where json.js can be found at: <URL: http://www.json.org/json.js >. If

Thanks for that link.

HS

RobG

unread,
Nov 13, 2006, 7:03:10 PM11/13/06
to

hobosa...@gmail.com wrote:
> RobG wrote:
> > If you are specifically after the above structure
>
> Nope, I'm after a big complicated structure that'll probably get bigger
> and more complicated later. But thanks!

Presumably you are only going to "join" properties with the same name
and type. The concept of your "join" only makes sense for Array or
String objects, I've shown an Array concat, adding a String concat
would be trivial.

What will you do with properties that are Boolean or Number (guessing
that Undefined and Null are irrelevant)?


--
Rob

hobosa...@gmail.com

unread,
Nov 14, 2006, 4:07:14 AM11/14/06
to
RobG wrote:
> Presumably you are only going to "join" properties with the same name
> and type. The concept of your "join" only makes sense for Array or
> String objects, I've shown an Array concat, adding a String concat
> would be trivial.

Well I've kicked this idea around my head for the last day or two, and
I think it's more complex than it seems at first glance. I have a
server generated config structure that I want to be merged with a
hardcoded default JS structure, but I don't want it to obliterate
nested config variables just because they weren't defined on the
server.

I'll call this function mergeJSON(arg1, arg2), take one structure and
copy it into another, overwriting values that exist, but making sure
not to lose any data unless necessary. For instance:

ret = mergeJSON( [ {"one": 1} ], [ {"one": 2} ] )

If we just looked at each argument's constructor, saw an array, and did
arg1.concat(arg2), we'd get:

[ {"one": 1}, {"one": 2} ]

But I want to look at ret[0].one and find the value defined in arg1, or
if it doesn't exist the value defined in arg2. In other words, I want
this:

[ {"one": 1} ]

So it's not as simple as it seems on the surface I think. It has to
recurse through each level of the hierarchy, working down to "leaf
nodes", merging everything manually rather than using concat.

> What will you do with properties that are Boolean or Number (guessing

Overwrite arg2 if they exist in both.

> that Undefined and Null are irrelevant)?

If undefined don't overwrite any data, but null is defined, so
overwrite just like a bool, number, string. I could concat strings but
it doesn't work in the context I need it. It would be trivial to concat
strings when a flag is passed though.

I'll post what I come up with if anyone's interested, in the meantime I
welcome feedback.

HS

hobosa...@gmail.com

unread,
Nov 14, 2006, 5:22:18 AM11/14/06
to
Stupid javascript doesn't pass by reference and keeps saying "too much
recursion". I want a real language. Maybe I can send it back to the
server with ajax and use perl. I have no idea how to get around the
"too much recursion", it says that even when only recursing once with a
single element array. Without recursion it becomes ugly and limited.
What's this no recursion thing? Surely javascript allows recursive
functions?

HS

Matt Kruse

unread,
Nov 14, 2006, 7:47:28 AM11/14/06
to
hobosa...@gmail.com wrote:
> Stupid javascript doesn't pass by reference

Sure it does, except for primitives.

> and keeps saying "too much
> recursion".

Then you have probably written bad code ;)

> I want a real language.

s/I want a real language/I want a language that acts how I expect it to, not
how it's defined/

> I have no idea how to get around the
> "too much recursion", it says that even when only recursing once with
> a single element array.

Then something is wrong.

It seems that you want some behavior that "makes sense", but you don't
really know what that is. Merging of data structures isn't as simple as you
might think.

First, write down requirements. How _exactly_ do you want the merge to
behave? When merging objects with common properties, which should get
priority? Should non-null values always overwrite null values? How should
arrays be treated? What about cases like
{x:1} and {x:[1]} ? What about {x:[1,2]} and {x:[2,1]} ?

Before you can write code, you need to clearly understand what you want. At
that point, people here can help you in tweaking the code you create.

--
Matt Kruse
http://www.JavascriptToolbox.com
http://www.AjaxToolbox.com


Richard Cornford

unread,
Nov 14, 2006, 8:19:14 AM11/14/06
to
Matt Kruse wrote:
> hobosa...@gmail.com wrote:
> > Stupid javascript doesn't pass by reference
>
> Sure it does, except for primitives.
>
> > and keeps saying "too much
> > recursion".
>
> Then you have probably written bad code ;)
<snip>

Erroneous code at minimum. As I recall (and it was a while ago now)
testing how much recursion prompted the "too much recursion" warning
came up with a figure of around 10,000 recursive calls.

Richard.

Randy Webb

unread,
Nov 14, 2006, 11:57:18 AM11/14/06
to
hobosa...@gmail.com said the following on 11/14/2006 5:22 AM:

> Stupid javascript doesn't pass by reference and keeps saying "too much
> recursion". I want a real language. Maybe I can send it back to the
> server with ajax and use perl.

And then you have to deal with "Stupid javascript" to be able to use
ajax to send it to the server. And if recursion is giving you a
headache, I want to be your supplier of headache medicine when you get
to the XMLHTTPRequest Object.

--
Randy
Chance Favors The Prepared Mind
comp.lang.javascript FAQ - http://jibbering.com/faq
Javascript Best Practices - http://www.JavascriptToolbox.com/bestpractices/

hobosa...@gmail.com

unread,
Nov 14, 2006, 2:02:59 PM11/14/06
to
Matt Kruse wrote:
> hobosa...@gmail.com wrote:
> > Stupid javascript doesn't pass by reference
>
> Sure it does, except for primitives.

I thought I would have to test for type to find out if I'm passing a
reference or not, something that javascript seems to suck at, but I
don't think I do.

> s/I want a real language/I want a language that acts how I expect it to, not
> how it's defined/

Yeah, basically. Another translation would be "Boo hoo I'm not as
comfortable writing JS as with other languages I've used 1000 times
more often...".

> > and keeps saying "too much
> > recursion".
>
> Then you have probably written bad code ;)

I think so. It was late and I don't even remember what I wrote, but I
don't have that problem now.

> First, write down requirements. How _exactly_ do you want the merge to
> behave? When merging objects with common properties, which should get
> priority? Should non-null values always overwrite null values? How should
> arrays be treated? What about cases like
> {x:1} and {x:[1]} ? What about {x:[1,2]} and {x:[2,1]} ?
>
> Before you can write code, you need to clearly understand what you want. At
> that point, people here can help you in tweaking the code you create.

I have my requirements very clear in my head, I don't think I'm very
good at communicating them on usenet though. I think I have what I want
here (I hope line wraps don't ruin this, please point out the best way
to post code if I screwed up):

function merge_json(merger, mergee) {
if (merger.constructor.toString().indexOf("Array") == -1 &&
merger.constructor.toString().indexOf("Object") == -1) {
//must have been passed non-JSON
return false;
}
for (i in merger) {
if (mergee[i]) {
if ((merger[i].constructor.toString().indexOf("Array") == -1 &&
merger[i].constructor.toString().indexOf("Object") == -1) ||
(mergee[i].constructor.toString().indexOf("Array") == -1 &&
mergee[i].constructor.toString().indexOf("Object") == -1)) {
//one or the other isn't an array/object
mergee[i] = merger[i];
} else {
merge_json(merger[i], mergee[i]);
}
} else {
mergee[i] = merger[i];
}
}
return true;
}

It seems to work, the super ugly if statements notwithstanding.

HS

hobosa...@gmail.com

unread,
Nov 14, 2006, 2:06:16 PM11/14/06
to
Randy Webb wrote:
> And then you have to deal with "Stupid javascript" to be able to use
> ajax to send it to the server. And if recursion is giving you a

I was kidding, but wouldn't it be nice to run perl in the browser?
Unless you're a user that cares about "security" or things like that.

HS

Matt Kruse

unread,
Nov 14, 2006, 3:18:11 PM11/14/06
to
hobosa...@gmail.com wrote:
>> s/I want a real language/I want a language that acts how I expect it
>> to, not how it's defined/
> Yeah, basically. Another translation would be "Boo hoo I'm not as
> comfortable writing JS as with other languages I've used 1000 times
> more often...".

Indeed, javascript has the ability to frustrate and confuse new users. But
it's not as bad as VBScript! (IMO).

I didn't check your code functionality or run any tests with it, but I
thought I would share a few comments:

> if (merger.constructor.toString().indexOf("Array") == -1 &&
> merger.constructor.toString().indexOf("Object") == -1) {

This is not robust, is it? What if I pass in an instance of Car, which is an
Object?

> if (mergee[i]) {

if mergee[i] has a value of false or 0, this will be false and it won't be
evaluated.
Instead you want
if (typeof mergee[i]!="undefined") {

VK

unread,
Nov 14, 2006, 4:31:38 PM11/14/06
to

hobosa...@gmail.com wrote:
> I have my requirements very clear in my head, I don't think I'm very
> good at communicating them on usenet though. I think I have what I want
> here (I hope line wraps don't ruin this, please point out the best way
> to post code if I screwed up):
>
> function merge_json(merger, mergee) {
> if (merger.constructor.toString().indexOf("Array") == -1 &&
> merger.constructor.toString().indexOf("Object") == -1) {
> //must have been passed non-JSON
> return false;
> }
> for (i in merger) {
> if (mergee[i]) {
> if ((merger[i].constructor.toString().indexOf("Array") == -1 &&
> merger[i].constructor.toString().indexOf("Object") == -1) ||
> (mergee[i].constructor.toString().indexOf("Array") == -1 &&
> mergee[i].constructor.toString().indexOf("Object") == -1)) {
> //one or the other isn't an array/object
> mergee[i] = merger[i];
> } else {
> merge_json(merger[i], mergee[i]);
> }
> } else {
> mergee[i] = merger[i];
> }
> }
> return true;
> }

So brute force speaking you want to implement a throughout exclusive
disjunction between the default JSON structure and the server-updated
JSON structure.
Two questions then (the default structure shall be called "Default" to
the rest of this document, the server-updated structure shall be called
"Updater" to the rest of this document):

1) Is it guaranteed that both Default and Updater are homostructural?
Other words, is it guaranteed that wherever Default has Object, Updater
has Object, wherever Default has Array, Updater has Array; so it cannot
be that on Object leaf of Default we may have the matching Array leaf
of Updated?

2) Do we have ... I almost wrote "homosetual" ... San Francisco
influence :-)... do we have matching array sizes and property sets in
Default and Updater? So say if a leaf in Default is Array, then Updater
will have the same number of elements (but possibly with other values
in some elements) And if we have an Object whan it will be possibly
different property values but the same property set?

0 new messages