vert.x
vert.x copied to clipboard
JsonObject and JsonArray in Java streams causing ClassCastException
Issue
While trying to sort a stream of JsonObject
s I encountered a ClassCastException
, due to missing interface Comparable
and thus missing implementation of compareTo
on JsonObject (and also JsonArray).
Without knowing if this interface was omitted on purpose from those two datatypes, I am raising the question, if a simple implementation of Comparable on both JsonObject
and JsonArray
could be provided, so at least we don't get a ClassCastException
, when working with Java Streams and alike?
Version
4.0.3
Reproduce JsonObject
ClassCastException
List l1 = List.of(new JsonObject().put("foo", "bar"), new JsonObject().put("fnord", "fnord"));
l1.stream().sorted().collect(Collectors.toList())
Reproduce JsonArray
ClassCastException
List l2 = List.of(new JsonArray(List.of("foo", "bar")), new JsonArray(List.of("fnord", "fnord")));
l2.stream().sorted().collect(Collectors.toList())
how do you compare two json objects or json array ?
@vietj tough question. Here my suggestion:
JsonObject
- Compare alphabetically sorted keys individually
- For each key call compareTo on their respective values.
if
result < 0
return result
else
go on with comparison -
throw ClassCastException
if some value is not implementing Comparable (this shouldn't be the case, as this change has to consist ofJsonObject
andJsonArray
Comparable
implementation)
JsonArray
- sort array with natural order
- compare individual elements with respective
compareTo
and proceed as mentioned above:if
result < 0
return result
else
go on with comparison -
throw ClassCastException
if some value is not implementing Comparable (this shouldn't be the case, as this change has to consist ofJsonObject
andJsonArray
Comparable
implementation)
As it may be odd to implement Comparable
on JsonObject
, due to the lack of an obvious interpretation of order, the benefit of having a deterministic way of somehow sorting and thus comparing two of above objects is hardly deniable.
Above proposal is merely an idea and frankly, it may not suite the vertx way of doing stuff.
Anyhow, a quick google search as well provides different approaches on achieving the goal in question via jackson's implementation: https://www.baeldung.com/jackson-compare-two-json-objects
ok, but here your example compares two JsonObject
or two JsonArray
which are not related to the order you describe
I'm having trouble getting your point.
See below a proof-of-concept for a possible JsonObject
Comparable implementation:
import io.vertx.core.json.JsonObject;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.stream.Collectors;
public class MyJsonObject extends JsonObject implements Comparable {
public MyJsonObject() {
super();
}
@Override
public MyJsonObject put(String key, Object value) {
super.put(key, value);
return this;
}
@Override
public int compareTo(@NotNull Object o) {
if(o instanceof MyJsonObject that) {
if(this.size() != that.size()) {
return this.size() - that.size();
}
List<String> thisKeys = this.getMap().keySet().stream().sorted().collect(Collectors.toList());
List<String> thatKeys = this.getMap().keySet().stream().sorted().collect(Collectors.toList());
for(int i = 0; i < thisKeys.size(); i++){
int comparedKey = thisKeys.get(i).compareTo(thatKeys.get(i));
if(comparedKey != 0) {
return comparedKey;
}
Object thisValue = this.getValue(thisKeys.get(i));
Object thatValue = this.getValue(thatKeys.get(i));
if(!thisValue.getClass().equals(thatValue.getClass())) {
return -1;
}
if(thisValue instanceof Comparable thisc) {
if(thatValue instanceof Comparable thatc) {
int comparedValue = thisc.compareTo(thatc);
if (comparedValue != 0) {
return comparedValue;
}
} else {
throw new ClassCastException("Incomparable types");
}
} else {
throw new ClassCastException("Incomparable types");
}
}
return 0;
} else {
throw new ClassCastException("Incomparable types");
}
}
}
MyJsonObject x = new MyJsonObject().put("bar", "foo").put("zeppelin", "xaver");
MyJsonObject y = new MyJsonObject().put("zeppelin", "xaver").put("foo", "bar");
MyJsonObject z = new MyJsonObject().put("zeppelin", "xaver").put("foo", "bar").put("alpha", "beta");
x.compareTo(y);
y.compareTo(x);
x.compareTo(z);
z.compareTo(x);
List.of(z,x,y).stream().sorted().collect(Collectors.toList());
yielding
class MyJsonObject
field MyJsonObject x = {"bar":"foo","zeppelin":"xaver"}
field MyJsonObject y = {"zeppelin":"xaver","foo":"bar"}
field MyJsonObject z = {"zeppelin":"xaver","foo":"bar","alpha":"beta"}
x.compareTo(y) = 0
y.compareTo(x) = 0
x.compareTo(z) = -1
z.compareTo(x) = 1
List.of(z,x,y).stream().sorted().collect(Collectors.toList()) = [{"bar":"foo","zeppelin":"xaver"}, {"zeppelin":"xaver","foo":"bar"}, {"zeppelin":"xaver","foo":"bar","alpha":"beta"}]