Merge JSON Objects: Jackson + BeanUtils.copyProperties

The Problem
We store some configuration object as JSON string and want client able to update separate fields directly - without reading it first.

The Solution
In our java configuration class, we use wrapper class for simple types, then Spring BeanUtils.copyProperties(Object source, Object target, String... ignoreProperties) to copy from new config to old config and ignore the null values in new config.

If the configuration has f1=1, f2=1, f3=1, the client only wants to change f3 to 3, then the client can change just send {f3: 3} without having to read the data first.

This is why we use wrapper instead of primitive fields, as the value would be null in new config if the client doesn't specify, we can add all null fields to the ignoreProperties.

public class SimpleConfig implements Serializable {
    private static final long serialVersionUID = 1L;
    private  Boolean featureAEnabled;
    private  Integer minVersion;
    // other fields ignored
}

public void mergeSimpleConfig(final SimpleConfig newConfig) {
    final String configName = COLUMN_SIMPLE_CONFIG;
    final SimpleConfig oldConfig = getSimpleConfig();
    SimpleConfig mergedConfig = (SimpleConfig) SerializationUtils.clone(oldConfig);
    mergeIgnoreNullProperties(newConfig, mergedConfig);

    final ConfigElement newConfigElement = new ConfigElement();
    newConfigElement.setConfigName(configName);
    newConfigElement.setValue(objectMapper.writeValueAsString(mergedConfig));
    newConfigElement.setLastUpdateTime(new Date());
    configDao.saveConfig(newConfigElement);
}

public static <T> void mergeIgnoreNullProperties(final T source, final T target) {
    BeanUtils.copyProperties(source, target, CobraUtil.getNullPropertyNames(source));
}
public static String[] getNullPropertyNames(final Object source) {
    final BeanWrapper src = new BeanWrapperImpl(source);
    final java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

    final Set<String> emptyNames = new HashSet<String>();
    for (final java.beans.PropertyDescriptor pd : pds) {
        final Object srcValue = src.getPropertyValue(pd.getName());
        if (srcValue == null) {
            emptyNames.add(pd.getName());
        }
    }
    final String[] result = new String[emptyNames.size()];
    return emptyNames.toArray(result);
}

@Component
@Provider
public class JerseyObjectMapperProvider implements ContextResolver<ObjectMapper> {
    private static ObjectMapper objectMapper = createFailSafeObjectmapper();

    @Override
    public ObjectMapper getContext(final Class<?> type) {
        return objectMapper;
    }
    public static ObjectMapper createFailSafeObjectmapper() {
        return new ObjectMapper()
                .setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"))
                .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
                .configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false)
                .configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false)
                .configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, false)
                .configure(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY, false)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .configure(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS, false)
                .setSerializationInclusion(Include.NON_NULL);
    }    
}
Read More
Jackson Essentials - the JSON Libaray
Using Jackson JSON View to Protect Mass Assignment Vulnerabilities
Merge JSON Objects: Jackson + BeanUtils.copyProperties
Jackson Generic Type + Java Type Erasure
Jackson Date Serialize + Deserialize
Post a Comment

Labels

Java (159) Lucene-Solr (110) Interview (61) All (58) J2SE (53) Algorithm (45) Soft Skills (36) Eclipse (34) Code Example (31) Linux (24) JavaScript (23) Spring (22) Windows (22) Web Development (20) Nutch2 (18) Tools (18) Bugs (17) Debug (15) Defects (14) Text Mining (14) J2EE (13) Network (13) PowerShell (11) Troubleshooting (11) Chrome (9) Design (9) How to (9) Learning code (9) Performance (9) UIMA (9) html (9) Http Client (8) Maven (8) Problem Solving (8) Security (8) bat (8) blogger (8) Big Data (7) Continuous Integration (7) Google (7) Guava (7) JSON (7) ANT (6) Coding Skills (6) Database (6) Scala (6) Shell (6) css (6) Algorithm Series (5) Cache (5) Dynamic Languages (5) IDE (5) Lesson Learned (5) Programmer Skills (5) Tips (5) adsense (5) xml (5) AIX (4) Code Quality (4) GAE (4) Git (4) Good Programming Practices (4) Jackson (4) Memory Usage (4) Miscs (4) OpenNLP (4) Project Managment (4) Spark (4) System Design (4) Testing (4) ads (4) regular-expression (4) Android (3) Apache Spark (3) Become a Better You (3) Concurrency (3) Eclipse RCP (3) English (3) Happy Hacking (3) IBM (3) J2SE Knowledge Series (3) JAX-RS (3) Jetty (3) Restful Web Service (3) Script (3) regex (3) seo (3) .Net (2) Android Studio (2) Apache (2) Apache Procrun (2) Architecture (2) Batch (2) Bit Operation (2) Build (2) Building Scalable Web Sites (2) C# (2) C/C++ (2) CSV (2) Career (2) Cassandra (2) Distributed (2) Fiddler (2) Firefox (2) Google Drive (2) Gson (2) How to Interview (2) Html Parser (2) Http (2) Image Tools (2) JQuery (2) Jersey (2) LDAP (2) Life (2) Logging (2) Python (2) Software Issues (2) Storage (2) Text Search (2) xml parser (2) AOP (1) Application Design (1) AspectJ (1) Chrome DevTools (1) Cloud (1) Codility (1) Data Mining (1) Data Structure (1) ExceptionUtils (1) Exif (1) Feature Request (1) FindBugs (1) Greasemonkey (1) HTML5 (1) Httpd (1) I18N (1) IBM Java Thread Dump Analyzer (1) JDK Source Code (1) JDK8 (1) JMX (1) Lazy Developer (1) Mac (1) Machine Learning (1) Mobile (1) My Plan for 2010 (1) Netbeans (1) Notes (1) Operating System (1) Perl (1) Problems (1) Product Architecture (1) Programming Life (1) Quality (1) Redhat (1) Redis (1) Review (1) RxJava (1) Solutions logs (1) Team Management (1) Thread Dump Analyzer (1) Visualization (1) boilerpipe (1) htm (1) ongoing (1) procrun (1) rss (1)

Popular Posts