Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/main/java/tools/jackson/databind/DeserializationContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -2268,6 +2268,23 @@ public DatabindException missingInjectableValueException(String msg,
valueId, forProperty, beanInstance);
}

// XXX JREF
public List<JRefResolver> getJRefResolvers() {
@SuppressWarnings("unchecked")
List<JRefResolver> jrefs = (List<JRefResolver>) getAttribute(JRefResolver.JREF_RESOLVER_LIST_CONTEXT_ATTR);
return jrefs == null?List.of():jrefs;
}

public void addJRefResolver(JRefResolver resolver) {
@SuppressWarnings("unchecked")
List<JRefResolver> jrefs = (List<JRefResolver>) getAttribute(JRefResolver.JREF_RESOLVER_LIST_CONTEXT_ATTR);
if (jrefs == null) {
// Lazy creation of JRefResolver list
jrefs = new ArrayList<JRefResolver>();
setAttribute(JRefResolver.JREF_RESOLVER_LIST_CONTEXT_ATTR, jrefs);
}
jrefs.add(resolver);
}
/*
/**********************************************************************
/* Other internal methods
Expand Down
226 changes: 226 additions & 0 deletions src/main/java/tools/jackson/databind/JRefModule.java
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);
}

}

}
52 changes: 52 additions & 0 deletions src/main/java/tools/jackson/databind/JRefPath.java
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 src/main/java/tools/jackson/databind/JRefResolveException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package tools.jackson.databind;

public class JRefResolveException extends RuntimeException {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be a JacksonException.

Copy link
Copy Markdown
Author

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?


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()
+ "]";
}

}
Loading
Loading