杰克逊中的通用元组反序列化

时间:2015-10-02 19:30:11

标签: java json generics jackson deserialization

碰巧我需要支持来自外部数据源的Java JSON数据。有一种常见的模式。它是一个包含固定数量的某些不同类型元素的数组。我们称之为元组。这是我使用FasterXML Jackson对3元素元组进行反序列化的示例,其中包含特定的预期元素类型:

public class TupleTest {
    public static void main(String[] args) throws Exception {
        String person = "{\"name\":\"qqq\",\"age\":35,\"address\":\"nowhere\",\"phone\":\"(555)555-5555\",\"email\":\"super@server.com\"}";
        String jsonText = "[[" + person + ",[" + person + "," + person + "],{\"index1\":" + person + ",\"index2\":" + person + "}]]";
        ObjectMapper om = new ObjectMapper().registerModule(new TupleModule());
        List<FixedTuple3> data = om.readValue(jsonText, new TypeReference<List<FixedTuple3>>() {});
        System.out.println("Deserialization result: " + data);
        System.out.println("Serialization result: " + om.writeValueAsString(data));
    }
}
class Person {
    public String name;
    public Integer age;
    public String address;
    public String phone;
    public String email;

    @Override
    public String toString() {
        return "Person{name=" + name + ", age=" + age + ", address=" + address
                + ", phone=" + phone + ", email=" + email + "}";
    }
}
class FixedTuple3 {
    public Person e1;
    public List<Person> e2;
    public Map<String, Person> e3;

    @Override
    public String toString() {
        return "Tuple[" + e1 + ", " + e2 + ", " + e3 + "]";
    }
}
class TupleModule extends SimpleModule {
    public TupleModule() {
        super(TupleModule.class.getSimpleName(), new Version(1, 0, 0, null, null, null));
        setSerializers(new SimpleSerializers() {
            @Override
            public JsonSerializer<?> findSerializer(SerializationConfig config,
                    JavaType type, BeanDescription beanDesc) {
                if (isTuple(type.getRawClass()))
                    return new TupleSerializer();
                return super.findSerializer(config, type, beanDesc);
            }
        });
        setDeserializers(new SimpleDeserializers() {
            @Override
            public JsonDeserializer<?> findBeanDeserializer(JavaType type,
                    DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException {
                Class<?> rawClass = type.getRawClass();
                if (isTuple(rawClass))
                    return new TupleDeserializer(rawClass);
                return super.findBeanDeserializer(type, config, beanDesc);
            }
        });
    }

    private boolean isTuple(Class<?> rawClass) {
        return rawClass.equals(FixedTuple3.class);
    }

    public static class TupleSerializer extends JsonSerializer<Object> {
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
            try {
                jgen.writeStartArray();
                for (int i = 0; i < 3; i++) {
                    Field f = value.getClass().getField("e" + (i + 1));
                    Object res = f.get(value);
                    jgen.getCodec().writeValue(jgen, res);
                }
                jgen.writeEndArray();
            } catch (Exception ex) {
                throw new IllegalStateException(ex);
            }
        }
    }   
    public static class TupleDeserializer extends JsonDeserializer<Object> {
        private Class<?> retClass;

        public TupleDeserializer(Class<?> retClass) {
            this.retClass = retClass;
        }

        public Object deserialize(JsonParser p, DeserializationContext ctx) throws IOException, JsonProcessingException {
            try {
                Object res = retClass.newInstance();
                if (!p.isExpectedStartArrayToken()) {
                    throw new JsonMappingException("Tuple array is expected but found " + p.getCurrentToken());
                }
                JsonToken t = p.nextToken();
                for (int i = 0; i < 3; i++) {
                    final Field f = res.getClass().getField("e" + (i + 1));
                    TypeReference<?> tr = new TypeReference<Object>() {
                        @Override
                        public Type getType() {
                            return f.getGenericType();
                        }
                    };
                    Object val = p.getCodec().readValue(p, tr);
                    f.set(res, val);
                }
                t = p.nextToken();
                if (t != JsonToken.END_ARRAY)
                    throw new IOException("Unexpected ending token in tuple deserializer: " + t.name());
                return res;
            } catch (IOException ex) {
                throw ex;
            } catch (Exception ex) {
                throw new IllegalStateException(ex);
            }
        }
    }
}

但是这种方法意味着我每次在特定大小的元组中面对新类型配置时都必须创建新类。所以我想知道是否有任何方法来定义反序列化器处理泛型类型。因此,每个元组大小就足以拥有一个元组类。例如,我的大小为3的通用元组可以定义为:

class Tuple3 <T1, T2, T3> {
    public T1 e1;
    public T2 e2;
    public T3 e3;

    @Override
    public String toString() {
        return "Tuple[" + e1 + ", " + e2 + ", " + e3 + "]";
    }
}

使用它看起来像:

List<Tuple3<Person, List<Person>, Map<String, Person>>> data = 
       om.readValue(jsonText, 
               new TypeReference<List<Tuple3<Person, List<Person>, Map<String, Person>>>>() {});

这是否可行?

2 个答案:

答案 0 :(得分:8)

确定。所以...可能有一种更简单的方法可以做到&#34; tuple&#34; -style。您实际上可以将POJO注释为序列化为数组:

@JsonFormat(shape=JsonFormat.Shape.ARRAY)
@JsonPropertyOrder({ "name", "age" }) // or use "alphabetic"
public class POJO {
   public String name;
   public int age;
}

如果是这样,他们将被编写为数组,从数组中读取。

但是如果你做了处理自定义泛型类型的东西,你可能需要解析类型参数。这可以使用TypeFactory方法findTypeParameters(...)来完成。虽然这看起来似乎是多余的,但如果您进行子类型(如果不是,JavaType实际上具有直接类型参数的访问器),则需要一般情况。

答案 1 :(得分:0)

是的,您必须使用Reflection获取所有字段,而不是按名称获取已知字段。

相关问题