杰克逊 - 通用getValue方法

时间:2016-09-15 14:47:29

标签: java json jackson

Jackson中的ValueNode的所有子类(JSON库)具有获取基础值对象的不同方法,例如: IntNodegetIntValueBooleanNodegetBooleanValue,依此类推。

为什么没有简称为getValue的通用/多态方法只返回ObjectObjectIntegerBoolean等等。,根据调用该方法的节点类型?

或者......实际上有这样的方法吗? 我需要这样的方法用于我的目的但似乎图书馆设计师没有发现添加这样的方法会有用。或者......出于某种原因,该方法是否故意丢失?

我的目的:在下面的代码中,我遍历树并生成仅由HashMapObject[]和Java基本类型组成的结构(如IntegerBoolean等)。如果我有这样的方法,而不是所有这些 if-else if-else if 阻止,我只需要一个方法调用(在{{ 1}}是叶节点,即JsonNode的子类型。但似乎我在杰克逊身上没有这样的方法。因此,我必须编写所有那些丑陋的 if-else if-else if 块。

CODE:

ValueNode

3 个答案:

答案 0 :(得分:4)

由于您要返回Object,因此可以使用此类

ObjectMapper mapper = new ObjectMapper();
return mapper.convertValue(nd,Object.class);

对于这个特定的测试用例,它起作用

        JsonNode nodeBool = BooleanNode.TRUE;
        Object objectBool = mapper.convertValue(nodeBool,Object.class);
        Boolean returnValBool = (Boolean) objectBool;
        System.err.println(returnValBool);

        JsonNode nodeDouble = new DoubleNode(3.4);
        Object objectDouble =    mapper.convertValue(nodeDouble,Object.class);
        Double returnValDouble = (Double) objectDouble;
        System.err.println(returnValDouble);

并且按预期结束了:

true
3.4

答案 1 :(得分:2)

这是一个有趣的问题:)

我不确定为什么没有通用方法。我相信这可能与他们不仅处理对象类型的事实有关吗?好吧,我没有资格证明他们的设计决定是合理的,但是我可以用你的通用方法来帮助你。

你是对的,你不能只调用value(),但你只能调用serialize(..,..)

更多细节:

所有值节点都拥有自己的值。所有值节点都知道如何序列化。我们可以利用这一点。

注意:这会有点hacky

您可以编写自己的序列化程序并让它存储您的值以检索它们:

public static class MyGen extends GeneratorBase {

        protected MyGen(int features, ObjectCodec codec) {
            super(features, codec);
        }

        private Object currentObject = null;

        @Override
        public void flush() throws IOException {
            // do nothing
        }

        @Override
        protected void _releaseBuffers() {
            // do nothing           
        }

        @Override
        protected void _verifyValueWrite(String typeMsg) throws IOException {
            // do nothing
        }

        @Override
        public void writeStartArray() throws IOException {
            // do nothing
        }

        @Override
        public void writeEndArray() throws IOException {
        }           // do nothing

        @Override
        public void writeStartObject() throws IOException {
            // do nothing
        }

        @Override
        public void writeEndObject() throws IOException {
            // do nothing
        }

        @Override
        public void writeFieldName(String name) throws IOException {
            // do nothing
        }

        @Override
        public void writeString(String text) throws IOException {
            currentObject = text;
        }

        @Override
        public void writeString(char[] text, int offset, int len) throws IOException {
            currentObject = new String(text);
        }

        @Override
        public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException {
            currentObject = new String(text);
        }

        @Override
        public void writeUTF8String(byte[] text, int offset, int length) throws IOException {
            currentObject = new String(text);
        }

        @Override
        public void writeRaw(String text) throws IOException {
            currentObject = new String(text);
        }

        @Override
        public void writeRaw(String text, int offset, int len) throws IOException {
            currentObject = new String(text);           
        }

        @Override
        public void writeRaw(char[] text, int offset, int len) throws IOException {
            currentObject = new String(text);           
        }

        @Override
        public void writeRaw(char c) throws IOException {
            currentObject = new Character(c);
        }

        @Override
        public void writeBinary(Base64Variant bv, byte[] data, int offset, int len) throws IOException {
            currentObject = bv;
        }

        @Override
        public void writeNumber(int v) throws IOException {
            currentObject = new Integer(v);
        }

        @Override
        public void writeNumber(long v) throws IOException {
            currentObject = new Long(v);
        }

        @Override
        public void writeNumber(BigInteger v) throws IOException {
            currentObject = v;
        }

        @Override
        public void writeNumber(double v) throws IOException {
            currentObject = new Double(v);
        }

        @Override
        public void writeNumber(float v) throws IOException {
            currentObject = new Float(v);
        }

        @Override
        public void writeNumber(BigDecimal v) throws IOException {
            currentObject = v;
        }

        @Override
        public void writeNumber(String encodedValue) throws IOException {
            currentObject = encodedValue;
        }

        @Override
        public void writeBoolean(boolean state) throws IOException {
            currentObject = new Boolean(state);
        }

        @Override
        public void writeNull() throws IOException {
            currentObject = null;
        }

    }

请注意,这是一个简单有趣的原型测试,可能需要更多考虑。

但有了这个,我可以做到:

public static void main(String[] args) throws JsonProcessingException, IOException {
        MyGen gen = new MyGen(0, new ObjectMapper());


        String test = "{ \"a\" : \"test\", \"b\" : 1, \"c\" : true, \"d\" : 2.5 }";

        JsonNode tree = new ObjectMapper().readTree(test);

        Impl instance = new DefaultSerializerProvider.Impl();

        ObjectMapper m = new ObjectMapper();

        for(JsonNode node : tree) {
            node.serialize(gen, instance);
            Object currentObject = gen.currentObject;
            System.out.println(currentObject);
        }

    }

打印哪些:

test
1
true
2.5

希望有帮助:)

阿图尔

Ps:另一个答案更短更好 - 但我认为这也很酷,所以无论如何我都会发布它

编辑:

关于你的一条评论。生成器将检索原始值,由您来保留该值。你将完全控制它。

答案 2 :(得分:0)

为解决此问题,我扩展了StdDeseralizer并创建了EnhancedDeserializer。 在反序列化过程中,调用deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)时,我只存储了objectMapper = (ObjectMapper) jsonParser.getCodec()。因为它实际上是ObjectMapper实例。 之后,我就可以打电话给objectMapper.convertValueMethod了。

public abstract class EnhancedDeserializer<T> extends StdDeserializer<T> {

protected ObjectMapper objectMapper;

public EnhancedDeserializer(final Class<?> clazz) {
    super(clazz);
}

@Override
public T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
    objectMapper = (ObjectMapper) jsonParser.getCodec(); // trick! the codec is the ObjectMapper
    return deserialize(objectMapper.readTree(jsonParser));
}

public abstract T deserialize(JsonNode node);

public <E> E readAs(JsonNode node, Class<E> clazz) {
    if (node == null) {
        return null;
    }
    return objectMapper.convertValue(node, clazz);
}

public <E> E readAs(JsonNode node, String key, Class<E> clazz) {
    if (node == null) {
        return null;
    }
    return readAs(node.get(key), clazz);
}

public <E> E readAs(JsonNode node, TypeReference<E> typeReference) {
    if (node == null) {
        return null;
    }
    return objectMapper.convertValue(node, typeReference);
}

public <E> E readAs(JsonNode node, String key, TypeReference<E> typeReference) {
    if (node == null) {
        return null;
    }
    return readAs(node.get(key), typeReference);
}

}

为了证明我有一个简单的对象。

public class MyObject {

    private Object fieldObject;

    private Long fieldLong;

    private String fieldString;

    private Map<String, Object> fieldWithGeneric;

    // getters setters omitted
}

使用EnhancedDeserializer进行反序列化的代码非常简单:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;

import java.util.Map;

public class MyObjectDeserializer extends EnhancedDeserializer<MyObject> {

    public MyObjectDeserializer() {
        super(MyObject.class);
    }

    @Override
    public MyObject deserialize(JsonNode node) {
        MyObject myObject = new MyObject();

        myObject.setFieldObject(readAs(node, "obj", Object.class));
        myObject.setFieldLong(readAs(node, "lng", Long.class));
        myObject.setFieldString(readAs(node, "str", String.class));
        myObject.setFieldWithGeneric(readAs(node, "map", new TypeReference<Map<String, Object>>() {
        }));

        return myObject;
    }
}

最后是测试:

public class MyObjectDeserializerTest {

    private ObjectMapper objectMapper;

    @Before
    public void setup() {
        objectMapper = new ObjectMapper()
                .registerModule(new SimpleModule()
                        .addDeserializer(MyObject.class, new MyObjectDeserializer()));
    }

    @Test
    public void deserialize_ObjectField_Integer() throws IOException {
        final MyObject myObject = objectMapper.readValue(aSimpleJsonField("obj", "1"), MyObject.class);
        assertThat(myObject.getFieldObject(), is(1));
    }

    @Test
    public void deserialize_ObjectField_String() throws IOException {
        final MyObject myObject = objectMapper.readValue(aSimpleJsonField("obj", "\"my-string\""), MyObject.class);
        assertThat(myObject.getFieldObject(), is("my-string"));
    }

    @Test
    public void deserialize_ObjectField_Double() throws IOException {
        final MyObject myObject = objectMapper.readValue(aSimpleJsonField("obj", "123.456"), MyObject.class);
        assertThat(myObject.getFieldObject(), is(123.456));
    }

    @Test
    public void deserialize_LongField() throws IOException {
        final MyObject myObject = objectMapper.readValue(aSimpleJsonField("lng", "456789123456789"), MyObject.class);
        assertThat(myObject.getFieldLong(), is(456789123456789L));
    }

    @Test
    public void deserialize_StringField() throws IOException {
        final MyObject myObject = objectMapper.readValue(aSimpleJsonField("str", "\"hello world\""), MyObject.class);
        assertThat(myObject.getFieldString(), is("hello world"));
    }

    @Test
    public void deserialize_genericField() throws IOException {
        final ObjectNode root = objectMapper.createObjectNode();
        final ObjectNode genericNode = objectMapper.createObjectNode();
        genericNode.put("str_0", "string");
        genericNode.put("int_1", 123);
        genericNode.put("long_2", 123456789123456L);
        genericNode.put("double_3", 987.654);
        genericNode.put("bool_4", true);
        root.set("map", genericNode);

        final MyObject myObject = objectMapper.readValue(objectMapper.writeValueAsString(root), MyObject.class);

        final Map<String, Object> map = myObject.getFieldWithGeneric();
        assertThat(map, notNullValue());
        assertThat(map.get("str_0"), is("string"));
        assertThat(map.get("int_1"), is(123));
        assertThat(map.get("long_2"), is(123456789123456L));
        assertThat(map.get("double_3"), is(987.654));
        assertThat(map.get("bool_4"), is(true));
    }

    private String aSimpleJsonField(String key, String value) {
        return "{ \"" + key + "\" : " + value + " }";
    }
}

通过放置EnhancedDeserializer,您的代码将非常简单,并且更重要的是类型安全的。现在,您可以删除raw unchecked抑制。

希望对您有帮助。