On 27/11/14 14:48, Attila Szegedi wrote:
So, some initial discussion of this with my team leads to following conclusions:

- We can't stop wrapping all objects in ScriptObjectMirror, as 
ScriptObjectMirror is a public class, and we allow people to expect it. If we 
now started returning ScriptObjectMirror sometimes and ArrayMirror (provisional 
name) other times, that'd be an API breaking change. That's sad, really – we 
should've probably never made ScriptObjectMirror public and instead forced 
people to only program against the JSObject interface instead.

- We've been thinking of creating a separate "class ArrayMirror implements JSObject, 
List<Object>" for wrapping JS Arrays, but you'd need to explicitly ask a mirror that'll return 
these transitively, e.g. we could give you a Java.toJSONCompatible(obj) API that you'd use as: 
"myObject.expectsJSON(Java.toJSONCompatible(someJson));" You'd still be getting a 
ScriptObjectMirror on the top level (as long as it ain't an array in which case the top level would 
itself be an ArrayMirror), but it'd be carrying a hidden flag that'd change its behavior so whenever you 
retrieve an Array from it, it gets wrapped into ArrayMirror and not ScriptObjectMirror. Also, if you 
retrieve an Object from it, you'd get a ScriptObjectMirror with this flag propagated, so Arrays at any 
nesting depth would always be exposed as Lists. Arguably, this could be the default behaviour except for 
the fact that it isn't how it worked since the initial 8 release and we can't break backwards 
compatibility…

How's that sound?


Sounds good. Thanks for taking time to look at this :)


Attila.

On Nov 27, 2014, at 2:46 PM, Attila Szegedi <[email protected]> wrote:

Also, the documentation for both List and Map interfaces prescribes an exact 
algorithm[1][2] that every implementation of them must use to calculate their 
hashCode(), and they too are incompatible. This is not as insurmountable as a javac 
error, but still not a good idea to violate. FWIW, having a separate ArrayMirror that 
implements only List<Object> might still be workable.

Attila.

---
[1] http://docs.oracle.com/javase/8/docs/api/java/util/List.html#hashCode--
[2] http://docs.oracle.com/javase/8/docs/api/java/util/Map.html#hashCode--

On Nov 27, 2014, at 2:40 PM, Attila Szegedi <[email protected]> wrote:

[...]

Unfortunately, we can't subclass ScriptObjectMirror to give you an ArrayMirror as no Java class can 
simultaneously implement both List and Map interfaces due to incompatibility in return types of 
"Object Map.remove(Object)" and "boolean List.remove(Object)" :-( Trust me, I 
was quite mad when I first realized this.

[...]

Attila.

On Nov 27, 2014, at 2:11 PM, Tim Fox <[email protected]> wrote:

As you know..

In JS, a JSON Object is represented by a JS object, and in the Java world it's often 
represented by Map<String, Object>.
In JS a JSON array is represented by a JS array, and in the Java world it's often 
represented by a List<Object>.

I'd love to be able to pass JSON between JS and Java and vice versa with the 
minimum of performance overhead. This is particularly important in Vert.x as we 
chuck a lot of JSON around.

Let's say I have a Java interface which expects some JSON:

interface SomeInterface {

  void expectsJSON(Map<String, Object> json);
}

Right now I am converting from JS-->Java as follows.

var someJson = { foo: "bar"};
String encoded = JSON.stringify(someJson);
Map<String, Object> map = SomeJavaJSONLibrary.decode(encoded);
myObject.expectsJSON(map);

As you can see it's pretty clunky. The other direction is equally as clunky. 
And it's slow as we're encoding/decoding everything via String.

Then I realised that if I pass a JS object directly into the expectsJSON method 
Nashorn will provide me with a Map<String, Object> that backs the original 
object. I.e. I can do this:

var someJson = { foo: "bar"};
myObject.expectsJSON(map);

Yay! No encoding overhead. Fast. :)

And it works with nested json:

var someJson = { foo: "bar", nested: { wibble: "blah"}};

Just when I was getting my hopes up that this would be a great super fast way 
of transferring JSON betwen Java and JS, I tried it with a nest array:

var someJson = { foo: "bar", nestedArray: [123, 456]};

But in Java, map.get("nestedArray") returns a ScriptObjectMirror not a List as 
I was hoping. :(

So.. passing from JS to Java: JS Object maps to Map, but JS Array maps to 
ScriptObjectMirror. (Seems a bit asymmetric?).

Any reason why we can't map JS Array to Java list when calling JS->Java? 
(Perhaps this is related to my previous question backing a JS Array with a List...)

Do you have any other suggestions for transferring JSON between JS and Java 
without too much encoding overhead?

Thanks again!









Reply via email to