Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
269 views
in Technique[技术] by (71.8m points)

java - Jackson API: partially update a string

I have a very complex Json object that I get as a String:

{ "a": ..., "b":..., /* lots of other properties */ "z":... }

that I read partially with Jackson and map into a Java class:

class PartialObjectForB { @JsonProperty("b") private ObjectB b; }

I use the readValue() method from the ObjectMapper class and get what I want... So far, so good.

Now, I want to update some values in PartialObjectForB and update the initial string I had. I figured how to update a Java object with jackson (by using readerForUpdating) but can't find how to do the opposite: update a Json object/string with a Java object.

I know how to solve quickly that problem by using JSONObject. For example, if I just want to update 1 value:

JSONObject j = new JSONObject(/* the full json string */);
j.getJSONObject("b").getJSONObject("bb")/* etc. */.put("bbbb", 4);
j.toString(); // will give me the full original text with only "b" updated.

But can't find how to do it with jackson.

Any idea?

Notes:

  • My input/output are strings, can't change that.
  • I don't know what data is in the json object. I just know that I may have the property "b" and that if I don't I can create it.
  • I may want to deserialize and update more than 1 property at the root level (e.g: "b", "h" and "w").
  • This problem is not recursive. Meaning: I have a full representation of the values I unserialize (no unknown properties).
  • The json object, as a string, is made of a few thousand bytes, but the piece(s) I want to update is usually a lot smaller (e.g: around 100 bytes).
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Full executable source with benchmark included is:

import java.io.IOException;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Random;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.Module;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.introspect.BasicBeanDescription;
import org.codehaus.jackson.map.ser.BeanPropertyWriter;
import org.codehaus.jackson.map.ser.std.BeanSerializerBase;
import org.codehaus.jackson.node.ObjectNode;
import org.json.JSONException;
import org.json.JSONObject;

public class JacksonModule {

    private static final ObjectMapper MAPPER = new ObjectMapper();
    private static final int COUNT = 0;
    private static final int REPEAT_HEADER = 40;

    static {
        MAPPER.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        MAPPER.configure(SerializationConfig.Feature.WRITE_NULL_PROPERTIES, false);
        MAPPER.registerModule(new MyModule());
    }
    private DataProcessor sdp;
    private long[] sum = new long[5];

    public static void main(String[] args) throws IOException, JSONException {
        new JacksonModule().start();
    }

    public JacksonModule() throws IOException, JSONException {
        this.sdp = new DataProcessor();
    }

    public void start() throws IOException, JSONException {
        run(-1, false); // load classes: slow
        if (COUNT > 0) {
            for (int i = 0; i < COUNT; ++i) {
                if (i % REPEAT_HEADER == 0) {
                    System.out.println("---------------------------------------------------------------------------------------");
                    print("", "RO JSONObject", "RO Jackson", "R/- Jackson", "R/W JSONObject", "R/W Jackson");
                    System.out.println("---------------------------------------------------------------------------------------");
                }
                run(i, true);
            }
            System.out.println("-- AVERAGE ----------------------------------------------------------------------------");
            print(1, sum[0] / COUNT, sum[1] / COUNT, sum[2] / COUNT, sum[3] / COUNT, sum[4] / COUNT);
            System.out.println("---------------------------------------------------------------------------------------");
            print("", "RO JSONObject", "RO Jackson", "R/- Jackson", "R/W JSONObject", "R/W Jackson");
            System.out.println("---------------------------------------------------------------------------------------");
        }
    }

    public void run(int i, boolean print) throws JSONException, IOException {
        long t1 = sdp.doReadWithJSONObject();
        long t2 = sdp.doReadWithJackson();
        long t3 = sdp.doReadForUpdatingWithJacksonButDontWrite();
        long t4 = sdp.doSomeWritingWithJSONObject();
        long t5 = sdp.doSomeWritingWithJackson();
        if (print) {
            print(i, t1, t2, t3, t4, t5);
            sum[0] += t1;
            sum[1] += t2;
            sum[2] += t3;
            sum[3] += t4;
            sum[4] += t5;
        }
    }

    private void print(int index, long t1, long t2, long t3, long t4, long t5) {
        print(Integer.toString(index), String.format("%,d", t1), String.format("%,d", t2), String.format("%,d", t3), String.format("%,d", t4), String.format("%,d", t5));
    }

    private void print(String i0, String a, String b, String c, String d, String e) {
        System.out.println("|"
                + StringUtils.leftPad(i0, 5) + "|"
                + StringUtils.leftPad(a, 15) + "|"
                + StringUtils.leftPad(b, 15) + "|"
                + StringUtils.leftPad(c, 15) + "|"
                + StringUtils.leftPad(d, 15) + "|"
                + StringUtils.leftPad(e, 15) + "|");
    }

    private static class DataProcessor {

        private DataStore store;
        private long t0, t1;

        private DataProcessor() throws IOException, JSONException {
            this.store = new DataStore(customer, browser);
        }

        public long doReadWithJSONObject() throws JSONException {
            t0 = System.nanoTime();
            JSONObject json = new JSONObject(store.readData(null)); // can throw JSONException
            JSONObject customer = json.getJSONObject("customer");  // can throw JSONException
            JSONObject browserInfo = json.getJSONObject("browser");  // can throw JSONException
            // need to do manually the mapping and figure out what is exactly in this object. Hell no!
            t1 = System.nanoTime();
            return t1 - t0;
        }

        public long doReadWithJackson() throws IOException {
            t0 = System.nanoTime();
            KnownPart obj = store.readData(null, KnownPart.class);
            t1 = System.nanoTime();
            return t1 - t0;
        }

        public long doReadForUpdatingWithJacksonButDontWrite() throws IOException {
            t0 = System.nanoTime();
            KnownPart obj = store.readDataForUpdating(null, KnownPart.class);
            t1 = System.nanoTime();
            return t1 - t0;
        }

        public long doSomeWritingWithJSONObject() throws JSONException {
            t0 = System.nanoTime();
            JSONObject json = new JSONObject(store.readData(null)); // can throw JSONException
            JSONObject customer = json.getJSONObject("customer");  // can throw JSONException
            JSONObject browserInfo = json.getJSONObject("browser");  // can throw JSONException
            customer.put("name", "Jackson Doe");
            browserInfo.put("version", "10");
            store.saveData(json);
            t1 = System.nanoTime();
            return t1 - t0;
        }

        public long doSomeWritingWithJackson() throws IOException {
            t0 = System.nanoTime();
            KnownPart obj = store.readDataForUpdating(null, KnownPart.class);
            obj.customer.name = "Jackson Doe";
            obj.browser.version = "10";
            store.saveData(obj);
            t1 = System.nanoTime();
            return t1 - t0;
        }
    }

    private static class DataStore {

        private final String data;

        private DataStore(Customer customer, BrowserInfo browser) throws IOException, JSONException {
            StringWriter sw = new StringWriter(1000);
            try (JsonGenerator jgen = MAPPER.getJsonFactory().createJsonGenerator(sw)) {
                jgen.writeStartObject();
                writeBunchOfProperties(jgen);
                jgen.writeFieldName("customer");
                jgen.writeRawValue(MAPPER.writeValueAsString(customer));
                writeBunchOfProperties(jgen);
                jgen.writeFieldName("browser");
                jgen.writeRawValue(MAPPER.writeValueAsString(browser));
                writeBunchOfProperties(jgen);
                jgen.writeEndObject();
            }
            this.data = sw.toString();
        }

        private void writeBunchOfProperties(JsonGenerator jgen) throws IOException {
            int c = new Random().nextInt(3) + 1;
            for (int i = 0; i < c; ++i) {
                jgen.writeFieldName(RandomStringUtils.random(10));
                jgen.writeRawValue(JSON_LONG);
            }
        }

        public String readData(String query) {
            return data;
        }

        private void saveData(String json) {
            // TODO
        }

        public void saveData(JSONObject json) {
            saveData(json.toString());
        }

        public void saveData(Object obj) throws IOException {
            saveData(MAPPER.writeValueAsString(obj));
        }

        public <T> T readData(String query, Class<T> clazz) throws IOException {
            return MAPPER.readValue(readData(query), clazz);
        }

        public <T extends UnknownPart> T readDataForUpdating(String query, Class<T> clazz) throws IOException {
            ObjectNode root = (ObjectNode) MAPPER.readTree(readData(query));
            T obj = MAPPER.readValue(root, clazz);
            obj.tree = root;
            return obj;
        }
    }

    private static abstract class UnknownPart {

         ObjectNode tree;
    }

    private static class KnownPart extends UnknownPart {

        @JsonProperty
        private Customer customer;
        @JsonProperty
        private BrowserInfo browser;
    }

    private static class Customer {

        @JsonProperty
        private int id;
        @JsonProperty
        private String name;
        @JsonProperty
        private Address[] addresses; // just to make it more complex for this example

        public Customer(int id, String name, Address[] addresses) {
            this.id = id;
            this.name = name;
            this.addresses = addresses;
        }

        public Customer() {
        }
    }

    private static class Address {

        @JsonProperty
        private String street;
        @JsonProperty
        private String city;

        public Address(String street, String city) {
            this.street = street;
            this.city = city;
        }

        public Address() {
        }
    }

    private static class BrowserInfo {

        @JsonProperty
        private String agent;
        @JsonProperty
        private String version;

        public BrowserInfo(String agent, String version) {
            this.agent = agent;
            this.version = version;
        }

        public BrowserInfo() {
        }
    }

    private static class MyModule extends Module {

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

        @Override
        public Version version() {
            return new Version(0, 0, 1, "SNAPSHOT");
        }

        @Override
        public void setupModule(Module.SetupContext context) {
            context.addBeanSerializerModifier(new org.codehaus.jackson.map.ser.BeanSerializerModifier() {
                private UnknownPartSerializer cs;

                @Override
                public JsonSerializer modifySerializer(SerializationConfig config, BasicBeanDescription beanDesc, JsonSerializer<?> serializer) {
                    return UnknownPart.class.isAssignableFrom(beanDesc.getBeanClass())
                       ? new UnknownPartSerializer((BeanSerializerBase) serializer)
                       : serializer;
                }
            });
        }
    }

    private static class UnknownPartSerializer extends BeanSerializerBase {

        public UnknownPartSerializer(BeanSerializerBase src) {
            super(src);
        }

        @Override
        public void serialize(Object bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
            UnknownPart up = (UnknownPart) bean;
            jgen.writeStartObject();
            serializeFields(up, jgen, provider);
            jgen.writeEndObject();
        }

        protected void serializeFields(UnknownPart bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationEx

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...