4

我有一个非常复杂的 Json 对象,我得到一个字符串:

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

我与杰克逊一起阅读部分内容并映射到Java类:

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

我使用 ObjectMapper 类中的 readValue() 方法并得到我想要的......到目前为止,一切都很好。

现在,我想更新 PartialObjectForB 中的一些值并更新我拥有的初始字符串。我想出了如何使用 jackson 更新 Java 对象(通过使用 readerForUpdating),但找不到相反的方法:使用 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 个字节)。
4

3 回答 3

2

胜利!:) 我现在有一个实现,它不是很简单。包括基准 Jackson 与 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 对象(PO​​JO 和更复杂的对象)。虽然使用 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: ?
        }

在更高级别,我希望能够执行以下操作:

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

不会丢失我保存时没有读取的数据。显然,我不想第二次读取数据,因为我是从远程主机读取的,我不想将它缓存在一些静态/实例映射中,因为我需要它在高并发环境。

所以解决办法,简单来说就是: - 先读取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;
    }

该模块仅处理 UnknownPart 对象: 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()); // 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;
            }
        }
    }

同时,我写了一个基准来确认(或不)这个解决方案对于大 Json 字符串比 JSONObject 更快......测试比较: - 使用 JSONObject 读取(没有映射) - 使用 Jackson 读取 - 使用树读取Jackson - 用 JSONObject 读/写 - 用 Ja​​ckson 读/写 它确实更快:)

在 1000 次迭代之后,不包括潜在的类加载或 JVM 所做的一些初始化,我在纳秒内得到:

-- 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|
---------------------------------------------------------------------------------------
于 2013-08-04T09:08:44.100 回答
2

包含基准的完整可执行源代码是:

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\"}}}";
}
于 2013-08-04T09:16:06.993 回答
1

我能想象的最简单的解决方案 - 将您的 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));
    }
}

上面的程序打印:

{"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]}

如果要更改路径上的内部属性,可以通过以下方式实现:

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

上面的程序打印:

{"b":{"bb":{"bbb":20}}}
{"b":{"bb":{"bbb":4}}}

正如我们所看到的:值发生了变化。但是这个解决方案有很大的缺点——我们必须反序列化所有的 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]}

我没有进行任何比较测试,但您可以测试哪种解​​决方案更适合您。

于 2013-07-31T21:33:52.590 回答