Skip to content

Commit bb1e213

Browse files
authored
Merge pull request #174 from gdgib/G2-1794-JacksonInjection
G2-1794 Jackson injection
2 parents 7afc8d0 + b6dadf5 commit bb1e213

File tree

6 files changed

+178
-0
lines changed

6 files changed

+178
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.g2forge.gearbox.serdes.injection;
2+
3+
import java.util.Map;
4+
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.fasterxml.jackson.databind.SerializationConfig;
7+
import com.fasterxml.jackson.databind.cfg.ContextAttributes;
8+
9+
public class HJackonInjection {
10+
public ObjectMapper with(ObjectMapper mapper, Map<String, ?> injectables) {
11+
final SerializationConfig initialSerializationConfig = mapper.getSerializationConfig();
12+
final ContextAttributes attributes = initialSerializationConfig.getAttributes();
13+
final ContextAttributes attributesWithInjectedValue = attributes.withSharedAttributes(injectables);
14+
final SerializationConfig serializationConfigWithInjectedValue = initialSerializationConfig.with(attributesWithInjectedValue);
15+
16+
mapper.setInjectableValues(null);
17+
18+
return mapper.setConfig(serializationConfigWithInjectedValue);
19+
}
20+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.g2forge.gearbox.serdes.injection;
2+
3+
import com.fasterxml.jackson.databind.DeserializationContext;
4+
import com.fasterxml.jackson.databind.InjectableValues;
5+
import com.fasterxml.jackson.databind.JsonMappingException;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.fasterxml.jackson.databind.SerializationConfig;
8+
import com.fasterxml.jackson.databind.SerializerProvider;
9+
import com.fasterxml.jackson.databind.cfg.ContextAttributes;
10+
import com.fasterxml.jackson.databind.exc.MissingInjectableValueExcepion;
11+
import com.g2forge.alexandria.java.type.ref.ITypeRef;
12+
13+
import lombok.Builder;
14+
import lombok.Data;
15+
import lombok.RequiredArgsConstructor;
16+
17+
@Data
18+
@Builder(toBuilder = true)
19+
@RequiredArgsConstructor
20+
public class JacksonInjectedValue<T> {
21+
protected final String id;
22+
23+
protected final ITypeRef<T> type;
24+
25+
protected final T fallback;
26+
27+
public JacksonInjectedValue(Class<?> owner, String name, ITypeRef<T> type, T fallback) {
28+
this(owner.getName() + "." + name, type, fallback);
29+
}
30+
31+
public JacksonInjectedValue(Class<T> type, T fallback) {
32+
this(type.getName(), ITypeRef.of(type), fallback);
33+
}
34+
35+
public ObjectMapper inject(ObjectMapper mapper, T value) {
36+
final SerializationConfig initialSerializationConfig = mapper.getSerializationConfig();
37+
final ContextAttributes attributes = initialSerializationConfig.getAttributes();
38+
final ContextAttributes attributesWithInjectedValue = attributes.withSharedAttribute(getId(), value);
39+
final SerializationConfig serializationConfigWithInjectedValue = initialSerializationConfig.with(attributesWithInjectedValue);
40+
41+
final InjectableValues existingInjectableValues = mapper.getInjectableValues();
42+
if (existingInjectableValues == null) mapper.setInjectableValues(new InjectableValues.Std().addValue(getId(), value));
43+
else if (!(existingInjectableValues instanceof InjectableValues.Std)) throw new IllegalArgumentException();
44+
else((InjectableValues.Std) existingInjectableValues).addValue(getId(), value);
45+
46+
return mapper.setConfig(serializationConfigWithInjectedValue);
47+
}
48+
49+
public T get(SerializerProvider provider) {
50+
final Object value = provider.getAttribute(getId());
51+
if (value == null) return getFallback();
52+
return getType().cast(value);
53+
}
54+
55+
public T get(DeserializationContext context) {
56+
final Object value;
57+
try {
58+
value = context.findInjectableValue(getId(), null, null, null, null);
59+
} catch (MissingInjectableValueExcepion e) {
60+
return getFallback();
61+
} catch (JsonMappingException e) {
62+
throw new IllegalStateException(e);
63+
}
64+
if (value == null) return getFallback();
65+
return getType().cast(value);
66+
}
67+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.g2forge.gearbox.serdes.injection;
2+
3+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
4+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
5+
import com.g2forge.alexandria.java.function.IFunction1;
6+
import com.g2forge.alexandria.java.type.ref.ATypeRef;
7+
8+
import lombok.Builder;
9+
import lombok.Data;
10+
import lombok.RequiredArgsConstructor;
11+
12+
@Data
13+
@Builder(toBuilder = true)
14+
@RequiredArgsConstructor
15+
@JsonSerialize(using = ExampleObjectSerializer.class)
16+
@JsonDeserialize(using = ExampleObjectDeserializer.class)
17+
public class ExampleObject {
18+
public static final JacksonInjectedValue<IFunction1<Integer, Integer>> PREPROCESSOR = new JacksonInjectedValue<>(ExampleObject.class, "preprocessor", new ATypeRef<IFunction1<Integer, Integer>>() {}, IFunction1.identity());
19+
20+
protected final Integer value;
21+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.g2forge.gearbox.serdes.injection;
2+
3+
import java.io.IOException;
4+
5+
import com.fasterxml.jackson.core.JacksonException;
6+
import com.fasterxml.jackson.core.JsonParser;
7+
import com.fasterxml.jackson.databind.DeserializationContext;
8+
import com.fasterxml.jackson.databind.JsonNode;
9+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
10+
11+
public class ExampleObjectDeserializer extends StdDeserializer<ExampleObject> {
12+
private static final long serialVersionUID = 2867578675754843411L;
13+
14+
public ExampleObjectDeserializer() {
15+
super(ExampleObject.class);
16+
}
17+
18+
@Override
19+
public ExampleObject deserialize(JsonParser parser, DeserializationContext context) throws IOException, JacksonException {
20+
final JsonNode node = parser.getCodec().readTree(parser);
21+
final int value = node.get("value").asInt();
22+
return new ExampleObject(ExampleObject.PREPROCESSOR.get(context).apply(value));
23+
}
24+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.g2forge.gearbox.serdes.injection;
2+
3+
import java.io.IOException;
4+
5+
import com.fasterxml.jackson.core.JsonGenerator;
6+
import com.fasterxml.jackson.core.JsonProcessingException;
7+
import com.fasterxml.jackson.databind.SerializerProvider;
8+
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
9+
10+
public class ExampleObjectSerializer extends StdSerializer<ExampleObject> {
11+
private static final long serialVersionUID = 7620126831613439913L;
12+
13+
public ExampleObjectSerializer() {
14+
super(ExampleObject.class);
15+
}
16+
17+
@Override
18+
public void serialize(ExampleObject value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException, JsonProcessingException {
19+
jsonGenerator.writeStartObject();
20+
jsonGenerator.writeNumberField("value", ExampleObject.PREPROCESSOR.get(provider).apply(value.getValue()));
21+
jsonGenerator.writeEndObject();
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.g2forge.gearbox.serdes.injection;
2+
3+
import org.junit.Test;
4+
5+
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.databind.JsonMappingException;
7+
import com.fasterxml.jackson.databind.ObjectMapper;
8+
import com.g2forge.alexandria.test.HAssert;
9+
10+
public class TestInjection {
11+
@Test
12+
public void noinjection() throws JsonMappingException, JsonProcessingException {
13+
final ExampleObject actual = new ExampleObject(0);
14+
final ObjectMapper mapper = new ObjectMapper();
15+
HAssert.assertEquals(actual, mapper.readValue(mapper.writeValueAsString(actual), ExampleObject.class));
16+
}
17+
18+
@Test
19+
public void increment() throws JsonMappingException, JsonProcessingException {
20+
final ObjectMapper mapper = ExampleObject.PREPROCESSOR.inject(new ObjectMapper(), x -> x + 1);;
21+
HAssert.assertEquals(new ExampleObject(2), mapper.readValue(mapper.writeValueAsString(new ExampleObject(0)), ExampleObject.class));
22+
}
23+
}

0 commit comments

Comments
 (0)