簡體   English   中英

傑克遜中的通用元組反序列化

[英]Generic tuple de-serialization in Jackson

碰巧我需要支持來自外部數據源的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>>>>() {});

它是否可行?

好。 所以...可能有一種更簡單的方法來做“元組”式。 您實際上可以將POJO注釋為序列化為數組:

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

如果是這樣,它們將被編寫為數組,從數組中讀取。

但是,如果您執行處理自定義泛型類型的操作,則可能需要解析類型參數。 這可以使用TypeFactory ,方法findTypeParameters(...)來完成。 雖然這看起來似乎是多余的,但如果您進行子類型(如果不是, JavaType實際上具有直接類型參數的訪問器)則需要一般情況。

是的,您必須使用Reflection獲取所有字段,而不是按名稱獲取已知字段。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM