-
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
Gson changes the order of PriorityQueue in java when using new Gson().toJson(priorityQueue) #2393
Comments
I think it's probably worth registering a |
Interestingly Gson even has a unit test for gson/gson/src/test/java/com/google/gson/functional/CollectionTest.java Lines 138 to 148 in 43396e4
However, this test seems to be flawed because the values in the input JSON data are already sorted. If you provide instead To provide a bit more context on the current Gson behavior: Gson does not have a dedicated adapter for
This is also not a problem specific to Gson, see this Stack Overflow question about So as Éamonn mentioned above, you will have to write a custom class PriorityQueueAdapterFactory implements TypeAdapterFactory {
public static final PriorityQueueAdapterFactory INSTANCE = new PriorityQueueAdapterFactory();
private PriorityQueueAdapterFactory() { }
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Class<?> rawType = typeToken.getRawType();
// Only handle PriorityQueue, let other factories handle all other types
if (rawType != PriorityQueue.class) {
return null;
}
Type type = typeToken.getType();
Type elementType;
// If raw type without type argument choose Object as element type (and let Gson
// get actual runtime type)
if (type instanceof Class<?>) {
elementType = Object.class;
} else {
ParameterizedType parameterizedType = (ParameterizedType) type;
// Get the element type argument, for example `String` for `PriorityQueue<String>`
elementType = parameterizedType.getActualTypeArguments()[0];
}
@SuppressWarnings("unchecked")
final TypeAdapter<Object> elementAdapter = (TypeAdapter<Object>) gson.getAdapter(TypeToken.get(elementType));
@SuppressWarnings("unchecked")
TypeAdapter<T> r = (TypeAdapter<T>) new TypeAdapter<PriorityQueue<Object>>() {
@Override
public void write(JsonWriter out, PriorityQueue<Object> value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
// Custom Comparator is unsupported because it would otherwise have to be serialized as well
if (value.comparator() != null) {
throw new IllegalArgumentException("Custom Comparator is not supported");
}
// Must manually sort elements because iteration order is unspecified
Object[] elements = value.toArray();
Arrays.sort(elements);
out.beginArray();
for (Object element : elements) {
elementAdapter.write(out, element);
}
out.endArray();
}
@Override
public PriorityQueue<Object> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
PriorityQueue<Object> queue = new PriorityQueue<>();
in.beginArray();
while (in.hasNext()) {
Object element = elementAdapter.read(in);
queue.add(element);
}
in.endArray();
return queue;
}
};
return r;
}
} (Have not extensively tested this yet) And then register it like this: Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(PriorityQueueAdapterFactory.INSTANCE)
.create(); However, as you might have noticed in the adapter code above this is not so efficient because it has to store all elements in an array and sort them before being able to write them to JSON. With all other solutions, such as calling You could use the adapter factory shown above, or maybe the better solution would be to switch to a regular @eamonnmcmanus, do you think it is worth considering adding explicit support for |
I think probably a much simpler This might be a candidate for I am not very keen on the idea of adding a |
For delegation you would normally use Directly trying to serialize the array probably works but there might be some subtle differences regarding which types are used. If you want to preserve the original element type you would have to create from a parameterized type representing |
I might not have thought this through completely, but anyway we would necessarily be talking about a I see that the |
Yes I think so; otherwise you cannot really perform delegation to my knowledge (except with
That would actually be an option; the delegate adapters don't have to be the same for serialization and deserialization (as long as they behave identically). But then it would have to construct a For deserialization it would have also been possible to delegate to the default Gson adapter for But at that point I thought it was easier and more transparent (showing that serialization and deserialization behave identically) to implement this serialization and deserialization logic manually.
The factory serializes the set in the format For what it is worth, the |
Gson version 2.9.1
Java / Android version
Java 17
IntelliJ IDEA 2023.1 (Ultimate Edition)
Build #IU-231.8109.175, built on March 28, 2023
Runtime version: 17.0.6+10-b829.5 aarch64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
macOS 13.0.1
GC: G1 Young Generation, G1 Old Generation
Memory: 4096M
Cores: 8
Used tools
Description
When i am printing a PriorityQueue using new Gson().toJson(priorityQueue) i get the order of the integers not in the correct way
Expected behavior
PrintingFromWhileLoop: 1
PrintingFromWhileLoop: 2
PrintingFromWhileLoop: 3
PrintingFromWhileLoop: 4
PrintingFromWhileLoop: 5
PrintingFromWhileLoop: 9
Actual behavior
MyPriorityQueueWithoutPrettyPrint: [1,2,5,4,3,9]
Reproduction steps
Queue mQueue = new PriorityQueue<>();
mQueue.offer(4);
mQueue.offer(2);
mQueue.offer(9);
mQueue.offer(1);
mQueue.offer(3);
mQueue.offer(5);
https://github.com/wincelee/MyLeetCodes/blob/c8c5e569dc8fec99070f68c568cc786833faaa6e/src/main/java/leet_code_quizes/easy/GsonPriorityQueueBug.java
The text was updated successfully, but these errors were encountered: