Jackson:使用点符号将JSON属性转换为嵌套对象

时间:2022-08-30 10:41:41

I have a JSON like this

JSON是这样的

{ "id":1, "name":"Jack", "parent.id":2 }

Note the dot on "parent.id" property

注意“父”上的点。id”属性

Is it possible to map those JSON to the following classes ?

是否可以将这些JSON映射到以下类?

class Child {
    private int id;
    private String name;

    private Parent parent;

    //getter and setter methods
}

class Parent {
    private int id;
    private String name;

    //getter and setter methods
}

So the mapping result would be similar to following statements:

因此,映射结果将类似于以下语句:

Parent parent = new Parent();
parent.setId(2);

Child child = new Child();
child.setId(1);
child.setName("Jack");
child.setParent(parent); // Here is the result

1 个解决方案

#1


13  

you can convert this

你可以把这

{ "id":1, "name":"Jack", "parent.id":2 }

into this

到这个

{ "id":1, "name":"Jack", "parent": { "id":2 } }

by using this

通过使用这个

// I'm using jQuery here 
$.fn.serializeObject = function() {
  var arrayData, objectData;
  arrayData = this.serializeArray();
  objectData = {};

  $.each(arrayData, function() {
    var value;

    if (this.value != null) {
      value = this.value;
    } else {
      value = '';
    }

    // search for "parent.id" like attribute
    if (this.name.indexOf('.') != -1) {
      var attrs = this.name.split('.');
      var tx = objectData;

      for (var i = 0; i < attrs.length - 1; i++) {
        if (objectData[attrs[i]] == undefined)
          objectData[attrs[i]] = {};
        tx = objectData[attrs[i]];
      }
      tx[attrs[attrs.length - 1]] = value;
    } else {
      if (objectData[this.name] != null) {
        if (!objectData[this.name].push) {
          objectData[this.name] = [objectData[this.name]];
        }

        objectData[this.name].push(value);
      } else {
        objectData[this.name] = value;
      }
    }
  });

  return objectData;
};

and then you can serialize your code by using JSON.serialize().

然后可以使用JSON.serialize()序列化代码。

if you are using Jackson, then you can deserialize the JSON request string by doing any of these:

如果您正在使用Jackson,那么您可以通过以下操作来反序列化JSON请求字符串:

1. create a custom Jackson deserialize module

1。创建自定义Jackson反序列化模块

2. parse the JSON yourself

2。解析JSON自己

public Child parseJackson(String jsonRequest) {
  // what we need
  ObjectMapper mapper;
  JsonNode root, parentNode;

  // your models
  Child child;
  Parent parent;

  // assign
  mapper = new ObjectMapper();
  root = mapper.readTree(jsonRequest); // deserialize JSON as tree
  parentNode = root.get("parent"); // get the "parent" branch

  // assign (again)
  child = mapper.readValue(root, Child.class);
  parent = mapper.readValue(parentNode, Parent.class);

  child.setParent(parent);

  return child;
}

the downside of this method is you have to parse for every single JsonRequest with nested objects and it will be messy when there's a complex nested structure. If this is a problem, I suggest you do the #3

这种方法的缺点是必须对每个带有嵌套对象的JsonRequest进行解析,如果有一个复杂的嵌套结构,就会很混乱。如果这是一个问题,我建议你做#3

3. create a custom Jackson ObjectMapper class to automate this process

3所示。创建一个定制的Jackson ObjectMapper类来自动化这个过程。

The idea is to build generic process for #2 so that it could handle any nested request.

其思想是为#2构建通用流程,以便它能够处理任何嵌套的请求。

public class CustomObjectMapper extends ObjectMapper {

  // here's the method you need
  @Override
  public <T> T readValue(String src, Class<T> type)
      throws IOException, JsonParseException, JsonMappingException {

    JsonNode root = this.readTree(src);
    try {
      return readNestedValue(root, type);
    } catch (InstantiationException | IllegalAccessException | IOException
        | IllegalArgumentException | InvocationTargetException e) {
      return super.readValue(src, type);
    }

  }

  // if you're using Spring, I suggest you implement this method as well
  // since Spring's MappingJacksonHttpMessageConverter class will call 
  // this method.
  @Override
  public <T> T readValue(InputStream src, JavaType type)
      throws IOException, JsonParseException, JsonMappingException {

    JsonNode root = this.readTree(src);
    try {
      return readNestedValue(root, (Class<T>) type.getRawClass());
    } catch (InstantiationException | IllegalAccessException | IOException
        | IllegalArgumentException | InvocationTargetException e) {
      return super.readValue(src, type);
    }

  }

  // we need this to recursively scan the tree node
  protected <T> T readNestedValue(JsonNode root, Class<T> type)
      throws InstantiationException, IllegalAccessException, IOException,
        IllegalArgumentException, InvocationTargetException {

    // initialize the object use ObjectMapper's readValue
    T obj = super.readValue(root, type);
    Iterator it = root.getFieldNames();
    while (it.hasNext()) {
      String name = (String) it.next();
      String camelCaseName = name.substring(0, 1).toUpperCase() + name.substring(1);
      JsonNode node = root.get(name);

      Field f;
      try {
        f = type.getDeclaredField(name);
      } catch (NoSuchFieldException e) {
        f = findFieldInSuperClass(name, type.getSuperclass());
      }
      // if no field found then ignore
      if (f == null) continue; 

      Method getter, setter;
      try {
        getter = type.getMethod("get" + camelCaseName);
      } catch (NoSuchMethodException e) {
        getter = findGetterInSuperClass("get" + camelCaseName, type.getSuperclass());
      }
      // if no getter found or it has been assigned then ignore
      if (getter == null || getter.invoke(obj) != null) continue;

      try {
        setter = type.getMethod("set" + camelCaseName);
      } catch (NoSuchMethodException ex) {
        setter = findSetterInSuperClass("set" + camelCaseName, type.getSuperclass(), f.getType());
      }
      // if no setter found then ignore
      if (setter == null) continue;

      setter.invoke(obj, readNestedValue(node, f.getType()));
    }

    return obj;
  }

  // we need this to search for field in super class
  // since type.getDeclaredField() will only return fields that in the class
  // but not super class
  protected Field findFieldInSuperClass(String name, Class sClass) {
    if (sClass == null) return null;
    try {
      Field f = sClass.getDeclaredField(name);
      return f;
    } catch (NoSuchFieldException e) {
      return findFieldInSuperClass(name, sClass.getSuperclass());
    }
  }

  protected Method findGetterInSuperClass(String name, Class sClass) {
    if (sClass == null) return null;
    try {
      Method m = sClass.getMethod(name);
      return m;
    } catch (NoSuchMethodException e) {
      return findGetterInSuperClass(name, sClass.getSuperclass());
    }
  }

  protected Method findSetterInSuperClass(String name, Class sClass, Class type) {
    if (sClass == null) return null;
    try {
      Method m = sClass.getMethod(name, type);
      return m;
    } catch (NoSuchMethodException e) {
      return findSetterInSuperClass(name, sClass.getSuperclass(), type);
    }
  }
}

If you're using Spring, then the final step is registering this class as Spring bean.

如果您正在使用Spring,那么最后一步是将这个类注册为Spring bean。

<mvc:annotation-driven>
    <mvc:message-converters>
      <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
         <property name="objectMapper">
            <bean class="x.y.z.CustomObjectMapper"/>
         </property>
      </bean>
    </mvc:message-converters>
  </mvc:annotation-driven>

with these set up you can easily use

有了这些设置,你可以很容易地使用。

@RequestMapping("/saveChild.json")
@ResponseBody
public Child saveChild(@RequestBody Child child) {
  // do something with child
  return child;
}

Hope this helps :)

希望这有助于:)

#1


13  

you can convert this

你可以把这

{ "id":1, "name":"Jack", "parent.id":2 }

into this

到这个

{ "id":1, "name":"Jack", "parent": { "id":2 } }

by using this

通过使用这个

// I'm using jQuery here 
$.fn.serializeObject = function() {
  var arrayData, objectData;
  arrayData = this.serializeArray();
  objectData = {};

  $.each(arrayData, function() {
    var value;

    if (this.value != null) {
      value = this.value;
    } else {
      value = '';
    }

    // search for "parent.id" like attribute
    if (this.name.indexOf('.') != -1) {
      var attrs = this.name.split('.');
      var tx = objectData;

      for (var i = 0; i < attrs.length - 1; i++) {
        if (objectData[attrs[i]] == undefined)
          objectData[attrs[i]] = {};
        tx = objectData[attrs[i]];
      }
      tx[attrs[attrs.length - 1]] = value;
    } else {
      if (objectData[this.name] != null) {
        if (!objectData[this.name].push) {
          objectData[this.name] = [objectData[this.name]];
        }

        objectData[this.name].push(value);
      } else {
        objectData[this.name] = value;
      }
    }
  });

  return objectData;
};

and then you can serialize your code by using JSON.serialize().

然后可以使用JSON.serialize()序列化代码。

if you are using Jackson, then you can deserialize the JSON request string by doing any of these:

如果您正在使用Jackson,那么您可以通过以下操作来反序列化JSON请求字符串:

1. create a custom Jackson deserialize module

1。创建自定义Jackson反序列化模块

2. parse the JSON yourself

2。解析JSON自己

public Child parseJackson(String jsonRequest) {
  // what we need
  ObjectMapper mapper;
  JsonNode root, parentNode;

  // your models
  Child child;
  Parent parent;

  // assign
  mapper = new ObjectMapper();
  root = mapper.readTree(jsonRequest); // deserialize JSON as tree
  parentNode = root.get("parent"); // get the "parent" branch

  // assign (again)
  child = mapper.readValue(root, Child.class);
  parent = mapper.readValue(parentNode, Parent.class);

  child.setParent(parent);

  return child;
}

the downside of this method is you have to parse for every single JsonRequest with nested objects and it will be messy when there's a complex nested structure. If this is a problem, I suggest you do the #3

这种方法的缺点是必须对每个带有嵌套对象的JsonRequest进行解析,如果有一个复杂的嵌套结构,就会很混乱。如果这是一个问题,我建议你做#3

3. create a custom Jackson ObjectMapper class to automate this process

3所示。创建一个定制的Jackson ObjectMapper类来自动化这个过程。

The idea is to build generic process for #2 so that it could handle any nested request.

其思想是为#2构建通用流程,以便它能够处理任何嵌套的请求。

public class CustomObjectMapper extends ObjectMapper {

  // here's the method you need
  @Override
  public <T> T readValue(String src, Class<T> type)
      throws IOException, JsonParseException, JsonMappingException {

    JsonNode root = this.readTree(src);
    try {
      return readNestedValue(root, type);
    } catch (InstantiationException | IllegalAccessException | IOException
        | IllegalArgumentException | InvocationTargetException e) {
      return super.readValue(src, type);
    }

  }

  // if you're using Spring, I suggest you implement this method as well
  // since Spring's MappingJacksonHttpMessageConverter class will call 
  // this method.
  @Override
  public <T> T readValue(InputStream src, JavaType type)
      throws IOException, JsonParseException, JsonMappingException {

    JsonNode root = this.readTree(src);
    try {
      return readNestedValue(root, (Class<T>) type.getRawClass());
    } catch (InstantiationException | IllegalAccessException | IOException
        | IllegalArgumentException | InvocationTargetException e) {
      return super.readValue(src, type);
    }

  }

  // we need this to recursively scan the tree node
  protected <T> T readNestedValue(JsonNode root, Class<T> type)
      throws InstantiationException, IllegalAccessException, IOException,
        IllegalArgumentException, InvocationTargetException {

    // initialize the object use ObjectMapper's readValue
    T obj = super.readValue(root, type);
    Iterator it = root.getFieldNames();
    while (it.hasNext()) {
      String name = (String) it.next();
      String camelCaseName = name.substring(0, 1).toUpperCase() + name.substring(1);
      JsonNode node = root.get(name);

      Field f;
      try {
        f = type.getDeclaredField(name);
      } catch (NoSuchFieldException e) {
        f = findFieldInSuperClass(name, type.getSuperclass());
      }
      // if no field found then ignore
      if (f == null) continue; 

      Method getter, setter;
      try {
        getter = type.getMethod("get" + camelCaseName);
      } catch (NoSuchMethodException e) {
        getter = findGetterInSuperClass("get" + camelCaseName, type.getSuperclass());
      }
      // if no getter found or it has been assigned then ignore
      if (getter == null || getter.invoke(obj) != null) continue;

      try {
        setter = type.getMethod("set" + camelCaseName);
      } catch (NoSuchMethodException ex) {
        setter = findSetterInSuperClass("set" + camelCaseName, type.getSuperclass(), f.getType());
      }
      // if no setter found then ignore
      if (setter == null) continue;

      setter.invoke(obj, readNestedValue(node, f.getType()));
    }

    return obj;
  }

  // we need this to search for field in super class
  // since type.getDeclaredField() will only return fields that in the class
  // but not super class
  protected Field findFieldInSuperClass(String name, Class sClass) {
    if (sClass == null) return null;
    try {
      Field f = sClass.getDeclaredField(name);
      return f;
    } catch (NoSuchFieldException e) {
      return findFieldInSuperClass(name, sClass.getSuperclass());
    }
  }

  protected Method findGetterInSuperClass(String name, Class sClass) {
    if (sClass == null) return null;
    try {
      Method m = sClass.getMethod(name);
      return m;
    } catch (NoSuchMethodException e) {
      return findGetterInSuperClass(name, sClass.getSuperclass());
    }
  }

  protected Method findSetterInSuperClass(String name, Class sClass, Class type) {
    if (sClass == null) return null;
    try {
      Method m = sClass.getMethod(name, type);
      return m;
    } catch (NoSuchMethodException e) {
      return findSetterInSuperClass(name, sClass.getSuperclass(), type);
    }
  }
}

If you're using Spring, then the final step is registering this class as Spring bean.

如果您正在使用Spring,那么最后一步是将这个类注册为Spring bean。

<mvc:annotation-driven>
    <mvc:message-converters>
      <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
         <property name="objectMapper">
            <bean class="x.y.z.CustomObjectMapper"/>
         </property>
      </bean>
    </mvc:message-converters>
  </mvc:annotation-driven>

with these set up you can easily use

有了这些设置,你可以很容易地使用。

@RequestMapping("/saveChild.json")
@ResponseBody
public Child saveChild(@RequestBody Child child) {
  // do something with child
  return child;
}

Hope this helps :)

希望这有助于:)