-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Initial Jref deserialization implementation #6014
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
Open
scottslewis
wants to merge
13
commits into
FasterXML:3.x
Choose a base branch
from
scottslewis:jref_deserialization
base: 3.x
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
06c5bcb
Initial checkin of JRefValueDeserializer/JRefResolver
scottslewis 2e59cee
Added JRefSetter and JRefSetterFunction types, simplified JRefPath and
scottslewis d7e2e6b
Removed JRefSetter and JRefSetterFunction in favor of new SetterFunction
scottslewis 805fc1b
Added support in ValueDeserializerModifier for using
scottslewis 962e0f2
Merge branch 'FasterXML:3.x' into jref_deserialization
scottslewis 36ef70c
Added jref handling to StringCollectionDeserializer. Added test case
scottslewis fbc2d63
Added test cases for Double, Float, Boolean, and String handling in
scottslewis d038844
Added more (passing) test cases
scottslewis 5fd7a24
Refactored for simplicity. Added string constants for "$ref" and
scottslewis dfe1eb2
Merge branch '3.x' into jref_deserialization
scottslewis 3416b68
Refactoring for simplification.
scottslewis 42efbaa
Merge branch 'jref_deserialization' of https://github.com/scottslewis…
scottslewis 3854fe9
Merge branch '3.x' into jref_deserialization
scottslewis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,226 @@ | ||
| package tools.jackson.databind; | ||
|
|
||
| import java.util.List; | ||
| import java.util.function.Function; | ||
|
|
||
| import tools.jackson.core.JacksonException; | ||
| import tools.jackson.core.JsonParser; | ||
| import tools.jackson.core.JsonToken; | ||
| import tools.jackson.databind.BeanDescription.Supplier; | ||
| import tools.jackson.databind.deser.BeanDeserializerBuilder; | ||
| import tools.jackson.databind.deser.ValueDeserializerModifier; | ||
| import tools.jackson.databind.deser.std.DelegatingDeserializer; | ||
| import tools.jackson.databind.introspect.BeanPropertyDefinition; | ||
| import tools.jackson.databind.jsontype.TypeDeserializer; | ||
| import tools.jackson.databind.module.SimpleModule; | ||
| import tools.jackson.databind.node.ObjectNode; | ||
| import tools.jackson.databind.node.TreeTraversingParser; | ||
| import tools.jackson.databind.type.ArrayType; | ||
| import tools.jackson.databind.type.CollectionLikeType; | ||
| import tools.jackson.databind.type.CollectionType; | ||
| import tools.jackson.databind.type.MapLikeType; | ||
| import tools.jackson.databind.type.MapType; | ||
| import tools.jackson.databind.type.ReferenceType; | ||
|
|
||
| public class JRefModule extends SimpleModule { | ||
|
|
||
| private static final long serialVersionUID = 1L; | ||
|
|
||
| public JRefModule() { | ||
| super("JRef"); | ||
| } | ||
|
|
||
| protected class JRefValueDeserializerModifier extends ValueDeserializerModifier { | ||
|
|
||
| private static final long serialVersionUID = 1L; | ||
|
|
||
| protected ValueDeserializer<?> makeJRefValueDeserializer(DeserializationConfig config, JavaType jt, | ||
| Supplier beanDescRef, ValueDeserializer<?> d) { | ||
| return new JRefValueDeserializer(d); | ||
| } | ||
|
|
||
| @Override | ||
| public ValueDeserializer<?> modifyArrayDeserializer(DeserializationConfig config, ArrayType valueType, | ||
| Supplier beanDescRef, ValueDeserializer<?> deserializer) { | ||
| return makeJRefValueDeserializer(config, valueType, beanDescRef, deserializer); | ||
| } | ||
|
|
||
| @Override | ||
| public ValueDeserializer<?> modifyCollectionDeserializer(DeserializationConfig config, CollectionType type, | ||
| Supplier beanDescRef, ValueDeserializer<?> deserializer) { | ||
| return makeJRefValueDeserializer(config, type, beanDescRef, deserializer); | ||
| } | ||
|
|
||
| @Override | ||
| public ValueDeserializer<?> modifyEnumDeserializer(DeserializationConfig config, JavaType type, | ||
| Supplier beanDescRef, ValueDeserializer<?> deserializer) { | ||
| return makeJRefValueDeserializer(config, type, beanDescRef, deserializer); | ||
| } | ||
|
|
||
| @Override | ||
| public ValueDeserializer<?> modifyCollectionLikeDeserializer(DeserializationConfig config, | ||
| CollectionLikeType type, Supplier beanDescRef, ValueDeserializer<?> deserializer) { | ||
| return makeJRefValueDeserializer(config, type, beanDescRef, deserializer); | ||
| } | ||
|
|
||
| @Override | ||
| public ValueDeserializer<?> modifyDeserializer(DeserializationConfig config, Supplier beanDescRef, | ||
| ValueDeserializer<?> deserializer) { | ||
| return makeJRefValueDeserializer(config, null, beanDescRef, deserializer); | ||
| } | ||
|
|
||
| @Override | ||
| public KeyDeserializer modifyKeyDeserializer(DeserializationConfig config, JavaType type, | ||
| KeyDeserializer deserializer) { | ||
| return super.modifyKeyDeserializer(config, type, deserializer); | ||
| } | ||
|
|
||
| @Override | ||
| public ValueDeserializer<?> modifyMapDeserializer(DeserializationConfig config, MapType type, | ||
| Supplier beanDescRef, ValueDeserializer<?> deserializer) { | ||
| return makeJRefValueDeserializer(config, type, beanDescRef, deserializer); | ||
| } | ||
|
|
||
| @Override | ||
| public ValueDeserializer<?> modifyMapLikeDeserializer(DeserializationConfig config, MapLikeType type, | ||
| Supplier beanDescRef, ValueDeserializer<?> deserializer) { | ||
| return makeJRefValueDeserializer(config, type, beanDescRef, deserializer); | ||
| } | ||
|
|
||
| @Override | ||
| public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, Supplier beanDescRef, | ||
| BeanDeserializerBuilder builder) { | ||
| return super.updateBuilder(config, beanDescRef, builder); | ||
| } | ||
|
|
||
| @Override | ||
| public List<BeanPropertyDefinition> updateProperties(DeserializationConfig config, Supplier beanDescRef, | ||
| List<BeanPropertyDefinition> propDefs) { | ||
| return super.updateProperties(config, beanDescRef, propDefs); | ||
| } | ||
|
|
||
| @Override | ||
| public ValueDeserializer<?> modifyReferenceDeserializer(DeserializationConfig config, ReferenceType type, | ||
| Supplier beanDescRef, ValueDeserializer<?> deserializer) { | ||
| return super.modifyReferenceDeserializer(config, type, beanDescRef, deserializer); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void setupModule(SetupContext context) { | ||
| super.setupModule(context); | ||
| context.addDeserializerModifier(new JRefValueDeserializerModifier()); | ||
| } | ||
|
|
||
| public static class JRefValueDeserializer extends DelegatingDeserializer { | ||
|
|
||
| void trace(String method, JRefValueDeserializer vds) { | ||
| System.out.println(new StringBuffer(method).append(".").append(vds.toString().toString())); | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "JRefValueDeserializer[delegate=" + _delegatee + ", class=" + _valueClass + "]"; | ||
| } | ||
|
|
||
| void trace(String method) { | ||
| trace(method, this); | ||
| } | ||
|
|
||
| public JRefValueDeserializer(ValueDeserializer<?> src) { | ||
| super(src); | ||
| } | ||
|
|
||
| protected class JRefFindResult { | ||
| JRefPath jrefPath; | ||
| JsonParser parser; | ||
| } | ||
|
|
||
| protected TreeTraversingParser createTreeTraversingParser(JsonNode node) { | ||
| TreeTraversingParser result = new TreeTraversingParser(node); | ||
| result.nextToken(); | ||
| return result; | ||
| } | ||
|
|
||
| /** | ||
| * Find JRef in parser input stream. | ||
| * | ||
| * @param p JsonParser to use | ||
| * @param ctxt the current context | ||
| * @param typeDeserializer optional typeDeserializer | ||
| * @return JRefFindResult with JRefPath either set to non null (path found), or | ||
| * set to null (meaning that the further deserialization should be | ||
| * undertaken with the parse given in JRefFindResult | ||
| */ | ||
| protected JRefFindResult findJRef(JsonParser p, DeserializationContext ctxt, | ||
| TypeDeserializer typeDeserializer) { | ||
| JRefFindResult result = new JRefFindResult(); | ||
| JsonToken tok = p.currentToken(); | ||
| if (tok == JsonToken.START_OBJECT) { | ||
| JsonNode n = ctxt.readTree(p); | ||
| if (n instanceof ObjectNode) { | ||
| ObjectNode on = (ObjectNode) n; | ||
| JsonNode valNode = on.get(JRefPath.JREF_REF); | ||
| if (valNode != null) { | ||
| String valStr = valNode.asString(); | ||
| if (valStr != null) { | ||
| result.jrefPath = new JRefPath(valStr, ctxt, getDelegatee(), typeDeserializer); | ||
| result.parser = p; | ||
| return result; | ||
| } | ||
| // JREF_REF found, but no/null path. This is a syntax error | ||
| throw DatabindException.from(p, "JRefPath detected on stream but path is null", null); | ||
| } | ||
| } | ||
| result.parser = createTreeTraversingParser(n); | ||
| } else { | ||
| result.parser = p; | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| /** | ||
| * Deserialize with jref handling. Calls | ||
| * {@link #findJRef(JsonParser, DeserializationContext, TypeDeserializer)} to | ||
| * look on the parser stream for objects with 1 key == '$ref'. If found then the | ||
| * JRefPath is returned. If not found then the func arg is applied with the | ||
| * appropriate parser returned in JRefFindResult.parser field. | ||
| * | ||
| * @param p | ||
| * @param ctxt | ||
| * @param typeDeserializer | ||
| * @param func | ||
| * @return | ||
| */ | ||
| protected Object deserializerWithJRef(JsonParser p, DeserializationContext ctxt, | ||
| TypeDeserializer typeDeserializer, Function<JsonParser, Object> func) { | ||
| Object result = null; | ||
| JRefFindResult findResult = findJRef(p, ctxt, typeDeserializer); | ||
| if (findResult.jrefPath != null) { | ||
| result = findResult.jrefPath; | ||
| } else { | ||
| result = func.apply(findResult.parser); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| @Override | ||
| public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) | ||
| throws JacksonException { | ||
| return deserializerWithJRef(p, ctxt, typeDeserializer, | ||
| ps -> super.deserializeWithType(ps, ctxt, typeDeserializer)); | ||
| } | ||
|
|
||
| @Override | ||
| public Object deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException { | ||
| return deserializerWithJRef(p, ctxt, null, ps -> super.deserialize(ps, ctxt)); | ||
| } | ||
|
|
||
| @Override | ||
| protected ValueDeserializer<?> newDelegatingInstance(ValueDeserializer<?> newDelegatee) { | ||
| return new JRefValueDeserializer(newDelegatee); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package tools.jackson.databind; | ||
|
|
||
| import java.util.Objects; | ||
|
|
||
| import tools.jackson.databind.jsontype.TypeDeserializer; | ||
|
|
||
| public class JRefPath { | ||
|
|
||
| public static final String JREF_REF = "$ref"; | ||
| private final DeserializationContext ctxt; | ||
| private final String path; | ||
| private final ValueDeserializer<?> deserializer; | ||
| private final TypeDeserializer typeDeserializer; | ||
|
|
||
| public JRefPath(String path, DeserializationContext ctxt, ValueDeserializer<?> deserializer, | ||
| TypeDeserializer typeDeserializer) { | ||
| Objects.requireNonNull(path, "path cannot be null"); | ||
| this.path = path; | ||
| Objects.requireNonNull(ctxt, "ctxt cannot be null"); | ||
| this.ctxt = ctxt; | ||
| Objects.requireNonNull(deserializer, "deserializer cannot be null"); | ||
| this.deserializer = deserializer; | ||
| this.typeDeserializer = typeDeserializer; | ||
| } | ||
|
|
||
| public JRefPath(String path, DeserializationContext ctxt, ValueDeserializer<?> deserializer) { | ||
| this(path, ctxt, deserializer, null); | ||
| } | ||
|
|
||
| public DeserializationContext getContext() { | ||
| return this.ctxt; | ||
| } | ||
|
|
||
| public String getPath() { | ||
| return this.path; | ||
| } | ||
|
|
||
| public ValueDeserializer<?> getValueDeserializer() { | ||
| return deserializer; | ||
| } | ||
|
|
||
| public TypeDeserializer getTypeDeserializer() { | ||
| return typeDeserializer; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "JRefPath[ctxt=" + ctxt + ", path=" + path + ", deserializer=" + deserializer + ", typeDeserializer=" | ||
| + typeDeserializer + "]"; | ||
| } | ||
|
|
||
| } |
39 changes: 39 additions & 0 deletions
39
src/main/java/tools/jackson/databind/JRefResolveException.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package tools.jackson.databind; | ||
|
|
||
| public class JRefResolveException extends RuntimeException { | ||
|
|
||
| private static final long serialVersionUID = 1L; | ||
| private final JRefResolver resolver; | ||
| private final Object root; | ||
|
|
||
| public JRefResolveException(JRefResolver resolver, Object root, String message, Throwable cause) { | ||
| super(message, cause); | ||
| this.resolver = resolver; | ||
| this.root = root; | ||
| } | ||
|
|
||
| public JRefResolveException(JRefResolver resolver, Object root, String message) { | ||
| super(message); | ||
| this.resolver = resolver; | ||
| this.root = root; | ||
| } | ||
|
|
||
| public JRefResolveException(JRefResolver resolver, String message) { | ||
| this(resolver, null, message); | ||
| } | ||
|
|
||
| public JRefResolver getResolver() { | ||
| return this.resolver; | ||
| } | ||
|
|
||
| public Object getRoot() { | ||
| return this.root; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "JRefResolveException [resolver=" + resolver + ", root=" + root + ", message=" + super.getMessage() | ||
| + "]"; | ||
| } | ||
|
|
||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
JacksonException.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.
OK I will make this change. There seemed to be use of DatabindException static methods...i.e. from, etc to create and throw appropriate JacksonExceptions. Should JRefResolveException be added to that somehow?