-
Notifications
You must be signed in to change notification settings - Fork 66
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
Implement JSON-B 3 Polymorphism #100
Conversation
bbac29e
to
75c5d15
Compare
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.
Did a full review now we agreed upon the design, it is mainly a few enhancements of the runtime and get back the user contract on some points but looks very close to be mergeable to me. Very good work and kudo to have enhanced the default polymorphism support which was not as great as we aim at!
@@ -31,6 +31,7 @@ | |||
import org.apache.johnzon.mapper.Mapper; |
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.
guess except the setMappingsFactory the rest of the diff is no more needed?
@@ -78,21 +78,28 @@ private static <T extends Annotation> T getIndirectAnnotation(final Class<T> api | |||
return null; | |||
} | |||
|
|||
public static <T extends Annotation> T getDirectAnnotation(final Class<?> clazz, final Class<T> api) { |
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.
not sure why there is a diff in this class, it is already on master and should stay private while the usage should be getAnnotation
in all the rest of the diff 🤔
return clazz.isAnnotationPresent(JsonbTypeInfo.class) || !getParentClassesWithTypeInfo(clazz).isEmpty(); | ||
} | ||
|
||
public List<Map.Entry<String, String>> getPolymorphismPropertiesToSerialize(Class<?> clazz, Collection<String> otherProperties) { |
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.
looks like the returned type should be an array of pairs (or Map.Entry
this part is a detail) instead of a list to make the iteration faster at runtime, wdyt?
} | ||
|
||
public List<Map.Entry<String, String>> getPolymorphismPropertiesToSerialize(Class<?> clazz, Collection<String> otherProperties) { | ||
List<Map.Entry<String, String>> entries = new ArrayList<>(); |
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.
why not a plain LinkedHashMap
which will enable to simplify the "found more than once" check and keep the ordering (will just need to sorted(reverse)
when converting to an array), wdyt? Overall idea is to make the intent to provide a partial object (generator usage) more explicit.
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 "TypeInfo key only once" check can actually be computed in advance, so I kept it as a List.
list.add(0, ...)
is way more convenient than converting a LinkedHashMap to a reversed Map.Entry<>[]
imho
* @throws JsonbException validation failed | ||
*/ | ||
private void validateOnlyOneParentWithTypeInfo(Class<?> classToValidate) { | ||
if (getParentClassesWithTypeInfo(classToValidate).size() > 1) { |
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.
this looks called too often (at least twice per type if I'm not mistaken, maybe reuse the same instance for both needs?)
src/site/markdown/index.md
Outdated
@@ -497,57 +497,6 @@ in `MessageDecoder`. | |||
} | |||
|
|||
|
|||
|
|||
### JSON-B Extra |
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.
maybe keep it and explain:
- very high level what is in the spec
- how to migrate from extras module to the standard impl
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.
don't know if migrating from jsonb-extras to jsonb3 will even be something users want to do - generated JSON format is different ({"_type": "bla", "_value": {"someValue": 1}}
vs {"_type": "bla", "someValue": 1}
)
Don't really want to add support for this format in jsonb module directly. It's out of spec and jsonb-extras was portable and is probably used on top of other jsonb impls
Maybe jsonb-extras shouldn't actually be deleted? It was pretty leightweight anyways
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.
:), fine to revert the deletion, while the feature is there all good for me.
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.
reverted the deletion + updated docs that extras polymorphism shouldn't be used anymore
} | ||
|
||
public Class<?> getTypeToDeserialize(JsonObject jsonObject, Class<?> clazz) { | ||
validateJsonbTypeInfo(clazz); |
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.
this should be done one per class somehow since it is a runtime class (compared to getPolymorphismPropertiesToSerialize which is a classmapping init computation only) so maybe use ClassMapping to do the validation only one, can likely become a Supplier<Class<?>>
using the supplier "pre" part to do the validation and supplier#get
to impl the runtime only (current line 91+).
} | ||
|
||
JsonValue typeValue = jsonObject.get(typeInfo.key()); | ||
if (!(typeValue instanceof JsonString)) { |
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.
typeValue != null && typeValue.getValueType() != STRING
is faster due to JVM instanceof
impl (to avoid when possible)
} | ||
|
||
String typeValueString = ((JsonString) typeValue).getString(); | ||
for (JsonbSubtype subtype : typeInfo.value()) { |
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.
should be a map for runtime probably
* @param clazz base class | ||
* @return List of classes with JsonbTypeInfo annotation | ||
*/ | ||
private List<Class<?>> getParentClassesWithTypeInfo(Class<?> clazz) { |
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.
wonder if the following pattern isnt more relevant (pseudo code):
final var superClass = clazz.getSuperclass();
if (superClass != null && hasTypeInfo(superClass)) {
return superClass;
}
return Stream.of(clazz.getInterfaces()).filter(it -> hasTypeInfo(it)).findFirst().orElse(null);
Overall idea is to avoid to create a list to keep only the first element. For interface case it will not change a lot but for parent case it will make it way more efficient. wdyt?
great work Markus, looking forward for this contribution! |
9911f6e
to
bf546fc
Compare
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.
a small autoformat fix to do and getTypeToDeserialize
to make runtime friendly and we are good to me
@@ -512,7 +512,9 @@ This module provides some extension to JSON-B. | |||
|
|||
#### Polymorphism | |||
|
|||
This extension provides a way to handle polymorphism: | |||
This extension shouldn't be used anymore if you don't absolutely rely on the JSON format it generates/parses. |
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.
What about "If your aim is only to be polymorphic you can use the built-in JSON-B feature but some cases remain unhandled and this extension enable them (mainly JSON-B to JSON-B cases like configurations and service to service calls but unlikely javascript to java cases)".
@@ -18,11 +18,16 @@ | |||
*/ | |||
package org.apache.johnzon.mapper; | |||
|
|||
import static java.util.Arrays.asList; |
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.
oops (autoformat)?
return result.toArray(Map.Entry[]::new); | ||
} | ||
|
||
public Class<?> getTypeToDeserialize(JsonObject jsonObject, Class<?> clazz) { |
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.
implemented like that this must not be called at runtime, overall consider any reflection is forbidden at runtime except to discover the passed instance type so this impl should be limited to (pseudo code) typeInfos.get(jsonObject.getString("..."))
so the reflection should have been stored in the handler earlier (likely in validate or getPolymorphismPropertiesToSerialize to avoid to do it > once) and the lookup shouldnt be a loop but a map access
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.
reworked and reflection free now. Except for Class#getName
, is using it okay? Or should we possibly completely move away from having the class as a parameter?
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.
this one is only in error case and not so costly so no worries for me
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.
Mappings diff can drop (autoformat?) then it looks good to me (early approval to avoid to need another pass just for style).
Very good work, thanks a lot for the effort!
@rmannibucau dropped mappings diff |
related discussion: https://lists.apache.org/thread/vok6wprktky7qkvwk79x68gz6mjcp7rb