-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Supporting Long numbers during desiralization #1267
base: main
Are you sure you want to change the base?
Conversation
…hout floating point
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed (or fixed any issues), please reply here (e.g. What to do if you already signed the CLAIndividual signers
Corporate signers
|
I signed it! |
CLAs look good, thanks! |
Would this break backward compatibility? |
@lyubomyr-shaydariv I don't think so. Currently every number in a json document is being deserialized as a Double. So, people have the code like this in their projects:
When we will start returning Longs, the same code will work. No problems in this code:
|
@artem-krutov Could you please give a more concrete example of the issue you're trying to fix? |
@lyubomyr-shaydariv sure. The issue reproduces when we have a POJO with a Map inside it. For example:
Imagine we have the following JSON object:
What will we have once we deserialize?
The age will be completely OK:
But what about parameters?
The line will fail, because Double will be returned. The same is for fingers. My PR fixes it – if we put double into a JSON, we will get double after deserializing. If we put Integer like in the example above, we will get integer after deserialization. |
The issues I can see with the patch: Issue: Introducing a new JSON token type: LONG_NUMBERGson follows the JSON standard that does not make a difference between integer and real numbers. That is why, I guess being not the inventor of Gson, switch ( jsonToken ) {
case NUMBER:
// do something
case ...
// do something
case ...
// do something
default:
throw new AssertionError(jsonToken); // This must never happen, an assertion, but all of my code will fail.
} Issue: Possible misinterpreting of
|
So, generally there are a lot of workarounds. The following approach could work in some cases:
But it won't work in case when you don't know which class you need. For example:
I could agree it's not a super popular way to deserialize, and you usually know which exact class you need. But this flow is possible.
How's that?
We introduced LONG_NUMBER, and we process it in any place where we can see it. Then, about The first doesn't look like a good option. When you develop an application you want to be as straight as it possible. If you can have only integer amount of children, why you need Converting doubles into integers looks better to me. We can live with it, but it would be better if we don't need to do any convertions. I do understand your reasons not to merge this PR. You sound confident and you are indeed, your comments looks solid. Honestly, I didn't take into account that Gson follows the JSON specifications. |
Ah, no-no, I meant not your code, but mine, for example: the code relies the finite and known-ahead set of JSON tokens, so introducing
Then there's probably something wrong with your mappings: you mentioned
Well, it depends on how your code is organized. It's nice to have your and for-a-library Data Transfer Object classes (say,
Yes, and this makes JSON be easier to implement. For example, if you'd have a delimitation between integers in reals in JSON, how would it affect JavaScript where JSON originates from? For example, not all
You can try to implement a By the way, a few words on my previous comment:
|
I'm afraid your code could start failing on each and every change of the underlying library if it's so tied with it. You are basically not supposed to rely on the library's innards, you are supposed to use it's API. Or, use adapters ;) Yes, these barriers between 3rd party and your own code – they are really cool, this is true. It's a good idea to have them for some complex peaces of code. But the whole idea of any library is to fit into your needs. If you want to do simple things like getting data from JSON object, I believe it's good when you just do it. Or, otherwise, your business logic would easily transform into a bunch of adapters for each and every thing.
This is exactly how I workarond that double/int thing right now, thank you. So, speaking generally, the biggest issue I can see with the current implementation – it is not consistent. You put integer and then you get double. We can debate for a long time and we both can propose reasonable solutions. Sometimes more reasonable and sometimes less :) But the fact is the fact – the behavior is not consistent. Don't include JavaScript stuff here. Gson is a barrier (or, adapter) between JavaScript's JSON and Java code. And I really want to get object exactly of the same type as I put into the structure. |
Well, You're missing the fact that
True, it is not 100% consistent, and it cannot be such: how can you distinguish between bytes, shorts, ints, longs then? If you put an Take a look: if you read Please don't get me wrong, but you should either have a proper mapping at declaration-site, or use
100% working method is storing type information along with numbers. Say [
{"type": "byte", "value": 1},
{"type": "short", "value": 1},
{"type": "long", "value": 1},
{"type": "double", "value": 1}
] or purely hypothetical (and the highlighter detects it) [1B, 1S, 1L, 1D] Looks crazy? It does. But this resolves the issue of not having the original type information. And this is not a very non-trivial case: take a look at |
@lyubomyr-shaydariv thank you for detailed answers. I agree that structures like I see your position. Though, a couple of comments.
I think this is handled by this code:
(https://github.com/google/gson/pull/1267/files#diff-0776314ca8e083ca3943426740d1dd69R452) GSON can understand when it peeks longs, there is So, there would not be (or should not be?) any mess with longs and doubles.
Yes, that's true. But all these data formats are stand for numbers without floating part. It's too narrow case because people do not usually use these types at all. It could be confusing, because we don't need even I also want you to get me right: I don't want to argue, all I want is work together to make the library a bit better. I agree there are a bunch of moments that are not obvious to me and could be really obvious to you. I just want your help in some points (and already got nice-detailed answers). As far as I understood, you stand for 2 points:
Am I right? Also a question about the point 1: do you think we don't need it at all, or this particular implementation needs to be revisited and improved? |
Let me quote and comment myself:
I was wrong. If we take a look at the grammar of JSON numbers specified at https://tools.ietf.org/html/rfc8259#section-6 , then it's clear that a number unlimited to its literal length. Even more, below the grammar, it's said:
This is where Gson final String json = "[3.141592653589793238462643383279, 1e400]";
System.out.println("As big decimals: " + arrayToString(gson.fromJson(json, BigDecimal[].class)));
System.out.println(" As doubles: " + arrayToString(gson.fromJson(json, Double[].class)));
System.out.println(" As numbers: " + arrayToString(gson.fromJson(json, Number[].class)));
System.out.println(" As objects: " + arrayToString(gson.fromJson(json, Object[].class))); private static String arrayToString(final Object[] array) {
final StringBuilder builder = new StringBuilder().append('[');
for ( int i = 0; i < array.length; i++ ) {
if ( i > 0 ) {
builder.append(',');
}
final Object e = array[i];
builder.append(String.valueOf(e));
if ( e != null ) {
builder.append('(').append(e.getClass().getSimpleName()).append(')');
}
}
return builder.append(']').toString();
} Output:
I think that the
In general, yes. The code above demonstrates this approach: you tell Gson what you (want to) expect there.
Yes. For example, you PR breaks the tests, for another example (as we have discussed on I think Gson can be improved without breaking backwards compatibility. I have an idea on that and I'll probably suggest a change tomorrow. |
Do you think this change is still needed or has this been resolved by #1290? |
Problem: every number is being deserialized as Double, even if it doesn't have floating point: https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java#L79
Solution: added supporting for Long numbers:
1.0
and1.2
will be deserialized as Doubles1
will be deserialized as Long.