-
Notifications
You must be signed in to change notification settings - Fork 853
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved JSON.stringify for wrapped java objects #824
Conversation
In general this looks good to me and makes sense. However recently we seem to have a few more people who are more intensively using Java interoperability than I am so perhaps they will have some comments. If we don't hear anything for a few days then IMO we should merge this. |
What happens in this case? var h = new java.util.HashMap();
// key1 !== key2 && key1.toString() === key2.toString()
h.put('0.0', 'a');
h.put(0, 'b');
h; // {0.0=b, 0.0=a}
JSON.stringify(h); // ??? |
What happens if
// SpiderMonkey
JSON.stringify(0n); // Uncaught TypeError: BigInt value can't be serialized in JSON |
@tuchida @gbrail Thanks for your feedback. I agree, we should discuss some edge cases and wait for feedback from other people.
// Rhino
JSON.stringify(0n); // org.mozilla.javascript.EvaluatorException: missing ) after argument list It seems, that bigints are not yet supported by Rhino. The current implementation treats The second example with the hashMap is also a bit tricky. I am experimenting with a key conversion to support also other key types than string (you may see in this commit FOCONIS@de34409#diff-fab16163e35411a29842a0afdd4592dbf45391d96ede85a2ac737744d10ef51bR89) Map h = HashMap();
// key1 !== key2 && key1.toString() === key2.toString()
h.put("0", "a");
h.put(0, 'b');
String js = "JSON.stringify(obj.x)\n";
testIt(js, h, "{\"0\":\"b\",\"0\":\"b\"}"); I know the result is not the one we excpect, but what should we do in the case, if we have mixed-type keys? My current use-case for Cheers |
Is that true? rhino/src/org/mozilla/javascript/NativeJSON.java Lines 307 to 315 in f1f2c58
What happens to NaN and ±Infinity if Number.toString is called?
I'm making this now. master...tuchida:es2020-bigint |
As BigInteger implements Number, it could be, that there are different code paths as for 'POJOs'. I just grepped the code and find very few places, where "BigInt" or "BigInteger" occured. And also the parse error of
Great |
Probably
|
|
@rPraml I'm sorry, I did not see this PR before submitting my similar changes. Here are some things I found when comparing this PR with #860, which has already been merged.
js> hm = new java.util.HashMap()
{}
js> o = new java.lang.Object()
java.lang.Object@133e16fd
js> os = o.toString()
java.lang.Object@133e16fd
js> hm.put(o, 'object')
null
js> hm.put(os, 'string')
null
js> hm
{java.lang.Object@133e16fd=object, java.lang.Object@133e16fd=string}
js> JSON.stringify(hm)
{"java.lang.Object@133e16fd":"string","java.lang.Object@133e16fd":"string"}
js> hm.get(o)
object
|
- Circular references are now caught - Enums and some Date and Time types are converted to strings Co-authored-by: Roland Praml <roland.praml@foconis.de>
- Circular references are now caught - Enums and some Date and Time types are converted to strings Co-authored-by: Roland Praml <roland.praml@foconis.de>
…roved # Conflicts: # src/org/mozilla/javascript/NativeJSON.java # src/org/mozilla/javascript/NativeJavaList.java
…o son-stringify-improved
Hello @tonygermano , I've updated my PR, maybe you can take a look at it and maybe take some ideas.
I removed that code.
I do not unwrap NativeMap/Lists, as they can passed directly to
I've added
I partially agree. I understand your arguments, but there may be a lot of java objects in the wild that needs to be converted. Currently, all my tests will pass. Maybe you have time to take a look at this. cheers |
value = ((Calendar)value).toInstant(); | ||
} | ||
if (value instanceof Temporal | ||
|| value instanceof UUID) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CHECKME: There are a lot of candidates, that may be converted with "toString". Locale, Duration, ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like how you did this. I was thinking almost the same thing. I still think the default should be to throw TypeErrors, but it should be easy to swap in this implementation. What do you think about another property on the JSON object that is a behavior selector for java objects between TypeError, javaConverter, empty object, or undefined, with this default implementation of javaConverter being provided?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is, to distinguish, when use "toString" and when throw a TypeError (or return undefined):
Some brainstorming:
Idea 1: IF obj.getClass().startsWith("java.") THEN return obj.toString ELSE throw TypeError
This will cover all java objects from the JRE. But there may be some custom add ons (e.g. joda-time) where toString makes also sense
Idea 2: IF isLiveConnect(obj) THEN return obj.toString ELSE throw TypeError
The idea is, to detect, if the object comes from outside (not from the org.mozilla.javascript package). In this case, toString may be the best choice.
Idea 3: The user has to provide an implementation to convert LiveConnect objects to JSON.
This is the idea, I mostly like.
We use a lot of Java-Beans and use Jackson on the java side. It would be great, if we produce the same result like jackson.
And the javaConverter already goes in that direction, but I'm not yet sure if this is the best solution for now.
We use a shared scope for several threads. So if one thread would modify the javaConverter (which should not be possible, as NativeJSON should be sealed), the other thread would see the modification.
What would you think, when we delegate all unconvertable objects to the WrapFactory and it can decide, what to do.
Object json = cx.getWrapFactory().javaToJson(value, indent, gap)
if (json != null) {
return json;
}
if (!Undefined.isUndefined(value)) {
throw ScriptRuntime.typeErrorById("msg.json.cant.serialize", value.getClass().getName());
}
Incorporated some changes from PR mozilla#824 for checking circular references. Changed default behavior of java objects that are not treated as containers to return the toString value by default rather than throwing a TypeError. Co-authored-by: Roland Praml <roland.praml@foconis.de>
Incorporated some changes from PR #824 for checking circular references. Changed default behavior of java objects that are not treated as containers to return the toString value by default rather than throwing a TypeError. Co-authored-by: Roland Praml <roland.praml@foconis.de>
closing this PR as it is obsolete now due #875 |
Adds better support for JSON.stringify when used with LiveConnect java objects.
Old behaviour:
New behaviour:
null
values