FYI, I filed it as an enhancement request so we don't lose track of it: https://bugs.openjdk.java.net/browse/JDK-8066773
On Nov 27, 2014, at 8:08 PM, Tim Fox <[email protected]> wrote: > 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!
