Java Jackson API:部分更新字符串

Java Jackson API:部分更新字符串,java,json,serialization,jackson,deserialization,Java,Json,Serialization,Jackson,Deserialization,我有一个非常复杂的Json对象,它是作为字符串获取的: { "a": ..., "b":..., /* lots of other properties */ "z":... } 我与Jackson一起阅读了部分内容,并将其映射到Java类: class PartialObjectForB { @JsonProperty("b") private ObjectB b; } 我使用ObjectMapper类中的readValue()方法并得到我想要的。。。到目前为止,一切顺利 现在,我想更新P

我有一个非常复杂的Json对象,它是作为字符串获取的:

{ "a": ..., "b":..., /* lots of other properties */ "z":... }
我与Jackson一起阅读了部分内容,并将其映射到Java类:

class PartialObjectForB { @JsonProperty("b") private ObjectB b; }
我使用ObjectMapper类中的readValue()方法并得到我想要的。。。到目前为止,一切顺利

现在,我想更新PartialObjectForB中的一些值,并更新我拥有的初始字符串。 我想到了如何用jackson更新Java对象(通过使用readerforupdate),但找不到相反的方法:用Java对象更新Json对象/字符串

我知道如何使用JSONObject快速解决这个问题。例如,如果我只想更新1个值:

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.
但我找不到如何与杰克逊合作

有什么想法吗

注:

  • 我的输入/输出是字符串,无法更改
  • 我不知道json对象中有什么数据。我只知道我可能有属性“b”,如果我没有,我可以创建它
  • 我可能希望在根级别反序列化和更新多个属性(例如:“b”、“h”和“w”)
  • 这个问题不是递归的。意思是:我有一个我取消序列化的值的完整表示(没有未知属性)
  • json对象作为一个字符串,由几千个字节组成,但我想要更新的部分通常要小得多(例如:大约100个字节)

我能想到的最简单的解决方案是将JSON反序列化到
Map
类中(例如LinkedHashMap)。请参见下面的示例:

import java.util.LinkedHashMap;

import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonProgram {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"a\":\"java.lang.Integer\",\"b\":\"time json\",\"c\":\"action json\",\"d\":[1,2,3]}";
        System.out.println(json);

        LinkedHashMap<String, Object> map = mapper.readValue(json, LinkedHashMap.class);
        map.put("b", "Override property or create new");

        System.out.println(mapper.writeValueAsString(map));
    }
}
如果要更改路径的内部属性,可以通过以下方式实现:

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;

import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonProgram {

    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"b\":{\"bb\":{\"bbb\":20}}}";
        System.out.println(json);

        LinkedHashMap<String, Object> map = mapper.readValue(json, LinkedHashMap.class);
        JsonUpdater updater = new JsonUpdater(map);
        updater.update(Arrays.asList("b", "bb", "bbb"), 4);

        System.out.println(mapper.writeValueAsString(map));
    }
}

class JsonUpdater {

    private LinkedHashMap<String, Object> jsonMap;

    public JsonUpdater(LinkedHashMap<String, Object> jsonMap) {
        this.jsonMap = jsonMap;
    }

    public boolean update(Collection<String> propertiesOnThePath, Object newValue) {
        LinkedList<String> path = new LinkedList<String>(propertiesOnThePath);
        String lastProperty = path.removeLast();

        LinkedHashMap<String, Object> objectMap = jsonMap;
        while (!path.isEmpty()) {
            String property = path.poll();
            if (!objectMap.containsKey(property)) {
                return false;
            }

            objectMap = (LinkedHashMap<String, Object>) objectMap.get(property);
        }

        if (!objectMap.containsKey(lastProperty)) {
            return false;
        }

        objectMap.put(lastProperty, newValue);

        return false;
    }
}
正如我们所看到的:值被改变了。但是这个解决方案有很大的缺点——我们必须反序列化所有JSON。几千字节的字符串对于Java来说不是问题,但是如果您真的想优化您的程序,您可以使用
ObjectNode
类和
ObjectMapper\readTree
方法。请参阅以下源代码:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class JacksonProgram {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"a\":\"java.lang.Integer\",\"b\":\"time json\",\"c\":\"action json\",\"d\":[1,2,3]}";
        System.out.println(json);

        ObjectNode jsonTree = (ObjectNode) mapper.readTree(json);
        jsonTree.put("b", "Override property or create new");
        System.out.println(jsonTree.toString());
    }
}
以上程序打印:

{"a":"java.lang.Integer","b":"time json","c":"action json","d":[1,2,3]}
{"a":"java.lang.Integer","b":"Override property or create new","c":"action json","d":[1,2,3]}
{"b":{"bb":{"bbb":20}}}
{"b":{"bb":{"bbb":4}}}
{"a":"java.lang.Integer","b":"time json","c":"action json","d":[1,2,3]}
{"a":"java.lang.Integer","b":"Override property or create new","c":"action json","d":[1,2,3]}
我没有做任何比较测试,但您可以测试哪种解决方案对您更有效。

胜利!:)我现在有了它的一个实现,它不是很简单。 包括基准测试Jackson vs JSONObject

问题和解决方案逐条描述:

首先,我在一个数据存储中有一个很大的json字符串,我想对其进行部分反序列化和更新。反序列化必须是部分的,但序列化已满,因此我不会丢失未反序列化的数据。 以下是我在示例中使用的对象:

   private static class KnownPart {

        @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() {
        }
    }
KnownPart对象包含的属性远不止2个,但我不知道到底是哪些属性,我不能保证跟踪哪些属性添加到json字符串中

我有一个数据存储,它的数据用Json编码。当前的实现使用JSONObject进行读/写,并支持jackson读取json并将其映射到“真实”Java对象(POJO和更复杂的对象)。 虽然使用jackson编写完整的Json字符串不是问题,但是使用只表示字符串一小部分的Java对象更新现有字符串要困难得多

数据存储问题:

    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) {
            // Not implemented
        }

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

        public void saveData(Object obj) throws IOException {
            // problem: ?
        }

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

        public <T> T readDataForUpdating(String query, Class<T> clazz) throws IOException {
            // problem: ?
        }
不会丢失保存时未读取的数据。 显然,我不想第二次读取数据,因为我正在从远程主机读取数据,我不想将其缓存在某个静态/实例映射中,因为我需要在高度并发的环境中仍然非常有效

因此,简单地说,解决方案是: -首先读取json字符串的树,并使用它将json对象反序列化为Java对象。 -存储反序列化对象(在KnowPart类中)并将树存储在某个父抽象类中 -编写一个Jackson模块来定制bean的序列化方式。该代码与原始代码几乎相同,不同之处在于,当写入KnownPart中的属性时,它的键被从UnknownPart对象中的树中移除,然后就可以轻松地写入未知部分

主要目标是:

    private static abstract class UnknownPart {

         ObjectNode tree;
    }

    private static class KnownPart extends UnknownPart {

        @JsonProperty
        private Customer customer;
        @JsonProperty
        private BrowserInfo browser;
    }
该模块仅处理未知零件对象: 私有静态类MyModule扩展模块{

        @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;
                }
            });
        }
    }

包含基准测试的完整可执行源代码为:

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, JsonGenerationException {
            final BeanPropertyWriter[] props;
            if (_filteredProps != null && provider.getSerializationView() != null) {
                props = _filteredProps;
            } else {
                props = _props;
            }
            int i = 0;
            try {
                for (final int len = props.length; i < len; ++i) {
                    BeanPropertyWriter prop = props[i];
                    if (prop != null) { // can have nulls in filtered list
                        prop.serializeAsField(bean, jgen, provider);
                        bean.tree.remove(prop.getName());
                    }
                }
                if (_anyGetterWriter != null) {
                    _anyGetterWriter.getAndSerialize(bean, jgen, provider);
                }
                Iterator<Entry<String, JsonNode>> it = bean.tree.getFields();
                while (it.hasNext()) {
                    Entry<String, JsonNode> e = it.next();
                    jgen.writeFieldName(e.getKey());
                    jgen.writeObject(e.getValue());
                }
            } catch (Exception e) {
                String name = (i == props.length) ? "[anySetter]"
                        : props[i].getName();
                wrapAndThrow(provider, e, bean, name);
            } catch (StackOverflowError e) {
                /* 04-Sep-2009, tatu: Dealing with this is tricky, since we do not
                 *   have many stack frames to spare... just one or two; can't
                 *   make many calls.
                 */
                JsonMappingException mapE = new JsonMappingException("Infinite recursion (StackOverflowError)", e);
                String name = (i == props.length) ? "[anySetter]"
                        : props[i].getName();
                mapE.prependPath(new JsonMappingException.Reference(bean, name));
                throw mapE;
            }
        }
    }
    private static Customer customer = new Customer(1, "John Doe", new Address[]{
        new Address("broadway av", "new york"),
        new Address("peachtree st", "atlanta")
    });
    private static BrowserInfo browser = new BrowserInfo("IE", "6.0");
    // some json found on the internet
    private static final String JSON_LONG = "{\"web-app\": {"
            + "\"servlet\": ["
            + "{"
            + "\"servlet-name\": \"cofaxCDS\","
            + "\"servlet-class\": \"org.cofax.cds.CDSServlet\","
            + "\"init-param\": {"
            + "\"configGlossary:installationAt\": \"Philadelphia, PA\","
            + "\"configGlossary:adminEmail\": \"ksm@pobox.com\","
            + "\"configGlossary:poweredBy\": \"Cofax\","
            + "\"configGlossary:poweredByIcon\": \"/images/cofax.gif\","
            + "\"configGlossary:staticPath\": \"/content/static\","
            + "\"templateProcessorClass\": \"org.cofax.WysiwygTemplate\","
            + "\"templateLoaderClass\": \"org.cofax.FilesTemplateLoader\","
            + "\"templatePath\": \"templates\","
            + "\"templateOverridePath\": \"\","
            + "\"defaultListTemplate\": \"listTemplate.htm\","
            + "\"defaultFileTemplate\": \"articleTemplate.htm\","
            + "\"useJSP\": false,"
            + "\"jspListTemplate\": \"listTemplate.jsp\","
            + "\"jspFileTemplate\": \"articleTemplate.jsp\","
            + "\"cachePackageTagsTrack\": 200,"
            + "\"cachePackageTagsStore\": 200,"
            + "\"cachePackageTagsRefresh\": 60,"
            + "\"cacheTemplatesTrack\": 100,"
            + "\"cacheTemplatesStore\": 50,"
            + "\"cacheTemplatesRefresh\": 15,"
            + "\"cachePagesTrack\": 200,"
            + "\"cachePagesStore\": 100,"
            + "\"cachePagesRefresh\": 10,"
            + "\"cachePagesDirtyRead\": 10,"
            + "\"searchEngineListTemplate\": \"forSearchEnginesList.htm\","
            + "\"searchEngineFileTemplate\": \"forSearchEngines.htm\","
            + "\"searchEngineRobotsDb\": \"WEB-INF/robots.db\","
            + "\"useDataStore\": true,"
            + "\"dataStoreClass\": \"org.cofax.SqlDataStore\","
            + "\"redirectionClass\": \"org.cofax.SqlRedirection\","
            + "\"dataStoreName\": \"cofax\","
            + "\"dataStoreDriver\": \"com.microsoft.jdbc.sqlserver.SQLServerDriver\","
            + "\"dataStoreUrl\": \"jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon\","
            + "\"dataStoreUser\": \"sa\","
            + "\"dataStorePassword\": \"dataStoreTestQuery\","
            + "\"dataStoreTestQuery\": \"SET NOCOUNT ON;select test='test';\","
            + "\"dataStoreLogFile\": \"/usr/local/tomcat/logs/datastore.log\","
            + "\"dataStoreInitConns\": 10,"
            + "\"dataStoreMaxConns\": 100,"
            + "\"dataStoreConnUsageLimit\": 100,"
            + "\"dataStoreLogLevel\": \"debug\","
            + "\"maxUrlLength\": 500}},"
            + "{"
            + "\"servlet-name\": \"cofaxEmail\","
            + "\"servlet-class\": \"org.cofax.cds.EmailServlet\","
            + "\"init-param\": {"
            + "\"mailHost\": \"mail1\","
            + "\"mailHostOverride\": \"mail2\"}},"
            + "{"
            + "\"servlet-name\": \"cofaxAdmin\","
            + "\"servlet-class\": \"org.cofax.cds.AdminServlet\"},"
            + ""
            + "{"
            + "\"servlet-name\": \"fileServlet\","
            + "\"servlet-class\": \"org.cofax.cds.FileServlet\"},"
            + "{"
            + "\"servlet-name\": \"cofaxTools\","
            + "\"servlet-class\": \"org.cofax.cms.CofaxToolsServlet\","
            + "\"init-param\": {"
            + "\"templatePath\": \"toolstemplates/\","
            + "\"log\": 1,"
            + "\"logLocation\": \"/usr/local/tomcat/logs/CofaxTools.log\","
            + "\"logMaxSize\": \"\","
            + "\"dataLog\": 1,"
            + "\"dataLogLocation\": \"/usr/local/tomcat/logs/dataLog.log\","
            + "\"dataLogMaxSize\": \"\","
            + "\"removePageCache\": \"/content/admin/remove?cache=pages&id=\","
            + "\"removeTemplateCache\": \"/content/admin/remove?cache=templates&id=\","
            + "\"fileTransferFolder\": \"/usr/local/tomcat/webapps/content/fileTransferFolder\","
            + "\"lookInContext\": 1,"
            + "\"adminGroupID\": 4,"
            + "\"betaServer\": true}}],"
            + "\"servlet-mapping\": {"
            + "\"cofaxCDS\": \"/\","
            + "\"cofaxEmail\": \"/cofaxutil/aemail/*\","
            + "\"cofaxAdmin\": \"/admin/*\","
            + "\"fileServlet\": \"/static/*\","
            + "\"cofaxTools\": \"/tools/*\"},"
            + ""
            + "\"taglib\": {"
            + "\"taglib-uri\": \"cofax.tld\","
            + "\"taglib-location\": \"/WEB-INF/tlds/cofax.tld\"}}}";
}
import java.io.IOException;
导入java.io.StringWriter;
导入java.util.Iterator;
导入java.util.Map.Entry;
导入java.util.Random;
导入org.apache.commons.lang3.RandomStringUtils;
导入org.apache.commons.lang3.StringUtils;
导入org.codehaus.jackson.jsongGenerationException;
导入org.codehaus.jackson.jsonggenerator;
导入org.codehaus.jackson.JsonNode;
导入org.codehaus.jackson.Version;
导入org.codehaus.jackson.annotate.JsonProperty;
导入org.codehaus.jackson.map.DeserializationConfig;
导入org.codehaus.jackson.map.JsonMappingException;
导入org.codehaus.jackson.map.JsonSerializer;
导入org.codehaus.jackson.map.Module;
导入org.codehaus.jackson.map.ObjectMapper;
导入org.codehaus.jackson.map.SerializationConfig;
导入org.codehaus.jackson.map.SerializerProvider;
导入org.codehaus.jackson.map.introspect.BasicBeanDescription;
导入org.codehaus.jackson.map.ser.BeanPropertyWriter;
导入org.codehaus.jackson.map.ser.std.beanserializarbase;
导入org.codehaus.jackson.node.ObjectNode;
导入org.json.JSONException;
导入org.json.JSONObject;
公共类JacksonModule{
私有静态最终ObjectMapper MAPPER=新ObjectMapper();
私有静态最终整数计数=0;
专用静态最终int REPEAT_头=40;
静止的{
configure(在未知属性上反序列化config.Feature.FAIL,false);
configure(SerializationConfig.Feature.WRITE\u NULL\u属性,false);
registerModule(新的MyModule());
}
专用数据处理器;
私有长[]和=新长[5];
公共静态void main(字符串[]args)抛出IOException、JSONException{
新JacksonModule().start();
}
公共JacksonModule()抛出IOException,JSONException{
this.sdp=新的数据处理程序
    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, JsonGenerationException {
            final BeanPropertyWriter[] props;
            if (_filteredProps != null && provider.getSerializationView() != null) {
                props = _filteredProps;
            } else {
                props = _props;
            }
            int i = 0;
            try {
                for (final int len = props.length; i < len; ++i) {
                    BeanPropertyWriter prop = props[i];
                    if (prop != null) { // can have nulls in filtered list
                        prop.serializeAsField(bean, jgen, provider);
                        bean.tree.remove(prop.getName()); // new
                    }
                }
                if (_anyGetterWriter != null) {
                    _anyGetterWriter.getAndSerialize(bean, jgen, provider);
                }
                // new:
                Iterator<Entry<String, JsonNode>> it = bean.tree.getFields();
                while (it.hasNext()) {
                    Entry<String, JsonNode> e = it.next();
                    jgen.writeFieldName(e.getKey());
                    jgen.writeObject(e.getValue());
                }
            } catch (Exception e) {
                String name = (i == props.length) ? "[anySetter]"
                        : props[i].getName();
                wrapAndThrow(provider, e, bean, name);
            } catch (StackOverflowError e) {
                /* 04-Sep-2009, tatu: Dealing with this is tricky, since we do not
                 *   have many stack frames to spare... just one or two; can't
                 *   make many calls.
                 */
                JsonMappingException mapE = new JsonMappingException("Infinite recursion (StackOverflowError)", e);
                String name = (i == props.length) ? "[anySetter]"
                        : props[i].getName();
                mapE.prependPath(new JsonMappingException.Reference(bean, name));
                throw mapE;
            }
        }
    }
-- AVERAGE ----------------------------------------------------------------------------
|    1|        860,560|        157,772|        234,654|      1,595,018|        488,427|
---------------------------------------------------------------------------------------
|     |  RO JSONObject|     RO Jackson|    R/- Jackson| R/W JSONObject|    R/W Jackson|
---------------------------------------------------------------------------------------
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, JsonGenerationException {
            final BeanPropertyWriter[] props;
            if (_filteredProps != null && provider.getSerializationView() != null) {
                props = _filteredProps;
            } else {
                props = _props;
            }
            int i = 0;
            try {
                for (final int len = props.length; i < len; ++i) {
                    BeanPropertyWriter prop = props[i];
                    if (prop != null) { // can have nulls in filtered list
                        prop.serializeAsField(bean, jgen, provider);
                        bean.tree.remove(prop.getName());
                    }
                }
                if (_anyGetterWriter != null) {
                    _anyGetterWriter.getAndSerialize(bean, jgen, provider);
                }
                Iterator<Entry<String, JsonNode>> it = bean.tree.getFields();
                while (it.hasNext()) {
                    Entry<String, JsonNode> e = it.next();
                    jgen.writeFieldName(e.getKey());
                    jgen.writeObject(e.getValue());
                }
            } catch (Exception e) {
                String name = (i == props.length) ? "[anySetter]"
                        : props[i].getName();
                wrapAndThrow(provider, e, bean, name);
            } catch (StackOverflowError e) {
                /* 04-Sep-2009, tatu: Dealing with this is tricky, since we do not
                 *   have many stack frames to spare... just one or two; can't
                 *   make many calls.
                 */
                JsonMappingException mapE = new JsonMappingException("Infinite recursion (StackOverflowError)", e);
                String name = (i == props.length) ? "[anySetter]"
                        : props[i].getName();
                mapE.prependPath(new JsonMappingException.Reference(bean, name));
                throw mapE;
            }
        }
    }
    private static Customer customer = new Customer(1, "John Doe", new Address[]{
        new Address("broadway av", "new york"),
        new Address("peachtree st", "atlanta")
    });
    private static BrowserInfo browser = new BrowserInfo("IE", "6.0");
    // some json found on the internet
    private static final String JSON_LONG = "{\"web-app\": {"
            + "\"servlet\": ["
            + "{"
            + "\"servlet-name\": \"cofaxCDS\","
            + "\"servlet-class\": \"org.cofax.cds.CDSServlet\","
            + "\"init-param\": {"
            + "\"configGlossary:installationAt\": \"Philadelphia, PA\","
            + "\"configGlossary:adminEmail\": \"ksm@pobox.com\","
            + "\"configGlossary:poweredBy\": \"Cofax\","
            + "\"configGlossary:poweredByIcon\": \"/images/cofax.gif\","
            + "\"configGlossary:staticPath\": \"/content/static\","
            + "\"templateProcessorClass\": \"org.cofax.WysiwygTemplate\","
            + "\"templateLoaderClass\": \"org.cofax.FilesTemplateLoader\","
            + "\"templatePath\": \"templates\","
            + "\"templateOverridePath\": \"\","
            + "\"defaultListTemplate\": \"listTemplate.htm\","
            + "\"defaultFileTemplate\": \"articleTemplate.htm\","
            + "\"useJSP\": false,"
            + "\"jspListTemplate\": \"listTemplate.jsp\","
            + "\"jspFileTemplate\": \"articleTemplate.jsp\","
            + "\"cachePackageTagsTrack\": 200,"
            + "\"cachePackageTagsStore\": 200,"
            + "\"cachePackageTagsRefresh\": 60,"
            + "\"cacheTemplatesTrack\": 100,"
            + "\"cacheTemplatesStore\": 50,"
            + "\"cacheTemplatesRefresh\": 15,"
            + "\"cachePagesTrack\": 200,"
            + "\"cachePagesStore\": 100,"
            + "\"cachePagesRefresh\": 10,"
            + "\"cachePagesDirtyRead\": 10,"
            + "\"searchEngineListTemplate\": \"forSearchEnginesList.htm\","
            + "\"searchEngineFileTemplate\": \"forSearchEngines.htm\","
            + "\"searchEngineRobotsDb\": \"WEB-INF/robots.db\","
            + "\"useDataStore\": true,"
            + "\"dataStoreClass\": \"org.cofax.SqlDataStore\","
            + "\"redirectionClass\": \"org.cofax.SqlRedirection\","
            + "\"dataStoreName\": \"cofax\","
            + "\"dataStoreDriver\": \"com.microsoft.jdbc.sqlserver.SQLServerDriver\","
            + "\"dataStoreUrl\": \"jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon\","
            + "\"dataStoreUser\": \"sa\","
            + "\"dataStorePassword\": \"dataStoreTestQuery\","
            + "\"dataStoreTestQuery\": \"SET NOCOUNT ON;select test='test';\","
            + "\"dataStoreLogFile\": \"/usr/local/tomcat/logs/datastore.log\","
            + "\"dataStoreInitConns\": 10,"
            + "\"dataStoreMaxConns\": 100,"
            + "\"dataStoreConnUsageLimit\": 100,"
            + "\"dataStoreLogLevel\": \"debug\","
            + "\"maxUrlLength\": 500}},"
            + "{"
            + "\"servlet-name\": \"cofaxEmail\","
            + "\"servlet-class\": \"org.cofax.cds.EmailServlet\","
            + "\"init-param\": {"
            + "\"mailHost\": \"mail1\","
            + "\"mailHostOverride\": \"mail2\"}},"
            + "{"
            + "\"servlet-name\": \"cofaxAdmin\","
            + "\"servlet-class\": \"org.cofax.cds.AdminServlet\"},"
            + ""
            + "{"
            + "\"servlet-name\": \"fileServlet\","
            + "\"servlet-class\": \"org.cofax.cds.FileServlet\"},"
            + "{"
            + "\"servlet-name\": \"cofaxTools\","
            + "\"servlet-class\": \"org.cofax.cms.CofaxToolsServlet\","
            + "\"init-param\": {"
            + "\"templatePath\": \"toolstemplates/\","
            + "\"log\": 1,"
            + "\"logLocation\": \"/usr/local/tomcat/logs/CofaxTools.log\","
            + "\"logMaxSize\": \"\","
            + "\"dataLog\": 1,"
            + "\"dataLogLocation\": \"/usr/local/tomcat/logs/dataLog.log\","
            + "\"dataLogMaxSize\": \"\","
            + "\"removePageCache\": \"/content/admin/remove?cache=pages&id=\","
            + "\"removeTemplateCache\": \"/content/admin/remove?cache=templates&id=\","
            + "\"fileTransferFolder\": \"/usr/local/tomcat/webapps/content/fileTransferFolder\","
            + "\"lookInContext\": 1,"
            + "\"adminGroupID\": 4,"
            + "\"betaServer\": true}}],"
            + "\"servlet-mapping\": {"
            + "\"cofaxCDS\": \"/\","
            + "\"cofaxEmail\": \"/cofaxutil/aemail/*\","
            + "\"cofaxAdmin\": \"/admin/*\","
            + "\"fileServlet\": \"/static/*\","
            + "\"cofaxTools\": \"/tools/*\"},"
            + ""
            + "\"taglib\": {"
            + "\"taglib-uri\": \"cofax.tld\","
            + "\"taglib-location\": \"/WEB-INF/tlds/cofax.tld\"}}}";
}