Skip to content

Instantly share code, notes, and snippets.

@UnquietCode
Last active April 8, 2019 19:06
Show Gist options
  • Select an option

  • Save UnquietCode/7954032 to your computer and use it in GitHub Desktop.

Select an option

Save UnquietCode/7954032 to your computer and use it in GitHub Desktop.

Revisions

  1. UnquietCode revised this gist Dec 14, 2013. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions Test.java
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,3 @@
    package com.sb.data.api;

    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.node.ObjectNode;

  2. UnquietCode revised this gist Dec 14, 2013. 1 changed file with 75 additions and 0 deletions.
    75 changes: 75 additions & 0 deletions Test.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,75 @@
    package com.sb.data.api;

    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.node.ObjectNode;

    import java.io.ByteArrayOutputStream;

    /**
    * @author Ben Fagin
    * @version 2013-12-13
    */
    public class Test {

    interface Point extends Model {
    int getX();
    void setX(int x);

    int getY();
    void setY(int y);

    Nub getNub();
    void setNub(Nub nub);
    }

    interface Nub extends Model {
    void setThing(int thing);
    int getThing();
    }

    public static void main(String[] args) throws Exception {
    final ObjectMapper jsonMapper = new ObjectMapper();
    jsonMapper.registerModule(new MapperModule());

    ObjectNode data = jsonMapper.createObjectNode();
    data.put("x", 100);
    data.put("y", 200);
    data.putObject("nub").put("thing", 300);

    Point point = jsonMapper.reader(Point.class).readValue(data);

    System.out.println(point.getX());
    System.out.println(point.getY());
    System.out.println(point.getNub().getThing());

    point.setX(111);
    point.setY(222);
    point.setNub(new Nub() {
    int val = 333;

    public void setThing(int thing) {
    val = thing;
    }

    public int getThing() {
    return val;
    }
    });

    ByteArrayOutputStream os = new ByteArrayOutputStream();

    System.out.println(point.getX());
    System.out.println(point.getY());
    System.out.println(point.getNub().getThing());

    jsonMapper.writerWithType(Point.class).writeValue(os, point);
    System.out.println(os.toString());
    os.reset();

    point.getNub().setThing(400);
    System.out.println(point.getNub().getThing());

    jsonMapper.writerWithType(Point.class).writeValue(os, point);
    System.out.println(os.toString());
    }
    }
  3. UnquietCode created this gist Dec 14, 2013.
    44 changes: 44 additions & 0 deletions MapperModule.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,44 @@
    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.core.Version;
    import com.fasterxml.jackson.databind.*;
    import com.fasterxml.jackson.databind.deser.Deserializers;
    import com.fasterxml.jackson.databind.node.ObjectNode;

    import java.io.IOException;
    import java.util.Iterator;

    /**
    * @author Ben Fagin
    * @version 2013-12-13
    */
    public class MapperModule extends Module {

    @Override
    public String getModuleName() {
    return "ModelMapper";
    }

    @Override
    public Version version() {
    return new Version(2, 0, 0, null, null, null);
    }

    @Override
    public void setupModule(SetupContext context) {
    context.addDeserializers(new Deserializers.Base() {
    public @Override JsonDeserializer<?> findBeanDeserializer(final JavaType type, DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException {
    if (Model.class.isAssignableFrom(type.getRawClass())) {
    return new JsonDeserializer<Object>() {
    public @Override Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    Iterator<ObjectNode> obj = jp.readValuesAs(ObjectNode.class);
    return ModelMapper.map(type.getRawClass(), obj.next());
    }
    };
    }

    return super.findBeanDeserializer(type, config, beanDesc);
    }
    });
    }
    }
    9 changes: 9 additions & 0 deletions Model.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    /**
    * Marker interface for all model objects.
    *
    * @author Ben Fagin
    * @version 2013-12-13
    */
    interface Model {
    // nothing here, as this is just a marker
    }
    78 changes: 78 additions & 0 deletions ModelMapper.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,78 @@
    import com.fasterxml.jackson.databind.*;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    import com.fasterxml.jackson.databind.node.POJONode;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

    import static com.google.common.base.Preconditions.checkArgument;
    import static com.google.common.base.Preconditions.checkNotNull;

    /**
    * @author Ben Fagin
    * @version 2013-12-13
    */
    public class ModelMapper {

    @SuppressWarnings("unchecked")
    public static <T> T map(Class<T> klaus, final ObjectNode data) {
    checkNotNull(data);
    checkArgument(klaus != null && klaus.isInterface(), "interfaces only please");

    return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{klaus}, new InvocationHandler() {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String property = method.getName().substring(3);
    property = new String(new char[]{property.charAt(0)}).toLowerCase() + property.substring(1);

    // getter
    if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) {
    return getProperty(data, property, method);
    }

    // setter
    if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) {
    data.putPOJO(property, args[0]);
    return null;
    }

    throw new RuntimeException("Not a property: "+method.getName());
    }
    });
    }

    private static Object getProperty(ObjectNode data, String property, Method method) {
    Class<?> returnType = method.getReturnType();
    JsonNode value = data.get(property);

    if (Integer.class.equals(returnType) || int.class.equals(returnType)) {
    return value.asInt();
    }

    else if (Double.class.equals(returnType) || double.class.equals(returnType)) {
    return value.asDouble();
    }

    else if (Long.class.equals(returnType) || long.class.equals(returnType)) {
    return value.asLong();
    }

    else if (Boolean.class.equals(returnType) || boolean.class.equals(returnType)) {
    return value.asBoolean();
    }

    else if (String.class.equals(returnType)) {
    return value.asText();
    }

    else if (returnType.isInterface() && Model.class.isAssignableFrom(returnType)) {
    if (value.isObject()) {
    return map(returnType, (ObjectNode) value);
    } else if (value.isPojo()) {
    return ((POJONode) value).getPojo();
    }
    }

    throw new RuntimeException("Unknown return type: "+returnType);
    }
    }