Сохранить часть JSON как String с JacksonDeserializer - PullRequest
0 голосов
/ 04 июля 2019

Я хочу сохранить часть JSON как строковое значение. Насколько я знаю, с аннотациями нет способа, но я не смог найти способ получить полное значение объекта / массива в виде строки.

Существует обходной путь, который работает, читая его как объект и мгновенно записывая его обратно в виде строки, используя ObjectMapper of Jackson. Вы можете себе представить, это ужасное решение для очень больших JSON.

public class DeserializeTest {

    private static ObjectMapper mapper;

    public static void main(String[] args) throws IOException {
        mapper = Jackson2ObjectMapperBuilder.json().build();
        mapper.findAndRegisterModules();

        SimpleModule module = new SimpleModule();
        module.addDeserializer(TestClassWrapper.class, new TestDeserializer());
        mapper.registerModule(module);

        String json = "{\"name\":\"testprop\", \"data\":[{\"prop\":\"test\"},{\"prop\":\"test1\"},{\"prop\":\"test2\"}]}";
        TestClassWrapper t = mapper.readValue(json, TestClassWrapper.class);

        // later in program, when i know the expected class
        TestClass o = unwrap(t, new TypeReference<ArrayList<Test2>>() {});
    }

    public static class TestClassWrapper {
        String name;
        String data;

        // removed getter and setter
    }

    public static class TestClass {
        String name;
        List<Test2> data;
        // removed getter and setter
    }

    public static class Test2 {
        String prop;
        // removed getter and setter
    }

    public static class TestDeserializer extends JsonDeserializer<TestClassWrapper> {

        @Override
        public TestClassWrapper deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {

            TestClassWrapper t = new TestClassWrapper();
            String key = p.getCurrentName();
            if (key == null) {
                p.nextToken();
                key = p.getCurrentName();
            }
            for (; key != null; key = p.nextFieldName()) {
                p.nextToken();
                switch (key) {
                    case "name":
                        t.name = p.getValueAsString();
                        break;
                    case "data":
                        // what i tried: 
                        System.out.println(p.getText()); // [
                        System.out.println(p.getValueAsString()); // NULL
                        System.out.println(p.getCurrentValue()); //NULL
                        System.out.println(p.getCurrentToken()); // [ TOKEN
                        System.out.println(p.getParsingContext().getCurrentValue()); // NULL
                        System.out.println(p.getParsingContext().toString()); // [0]
                        System.out.println(p.getEmbeddedObject()); // NULL
                        System.out.println(p.getTextCharacters()); // [
                        try {
                            System.out.println(ctxt.readValue(p, String.class)); // MismatchedInputException
                        } catch (MismatchedInputException e){}

                        // The only way i could make it work.
                        // Parse to a object and write it back as string.
                        StringBuilder sb = new StringBuilder();
                        Iterator<Object> it = p.readValuesAs(Object.class);
                        while (it.hasNext()) {
                            sb.append(mapper.writeValueAsString(it.next()));
                            sb.append(it.hasNext() ? "," : "");
                        }
                        t.data = p.getCurrentToken() == JsonToken.END_ARRAY ? "[" + sb.toString() + "]" : sb.toString();
                        break;
                }
            }

            return t;
        }
    }

    public static TestClass unwrap(TestClassWrapper t, TypeReference targetClass) throws IOException {
        TestClass o = new TestClass();
        o.name = t.name;
        o.data = mapper.readValue(t.data, targetClass);
        return o;
    }
}

Как я могу сказать объекту JsonParser, чтобы он просто дал мне строку текущего значения?

(для data это будет: "[{"prop":"test"}, {"prop":"test1"}, {"prop":"test2"}]")

...