首页 > 解决方案 > Spring MVC:如何在反序列化包含重复对象定义的 JSON 字符串时创建对象的单个实例

问题描述

我想使用 Spring MVC 将包含重复定义的 JSON 字符串反序列化为 java 对象。要求是为这些重复定义创建一个对象的单个实例,而不是创建具有相同内容的重复对象。

示例类:

import java.io.Serializable;

public class Example implements Serializable {
    private String a;
    private String b;

    public String getA() {
        return a;
    }

    public void setA(String a) {
        this.a = a;
    }

    public String getB() {
        return b;
    }

    public void setB(String b) {
        this.b = b;
    }
}

JSON字符串:

[
  {
    "a": "a",
    "b": "b"
  },
  {
    "a": "a",
    "b": "b"
  },
  {
    "a": "a1",
    "b": "b1"
  }
]

我得到的输出:

 ExampleList=[
    Example@151c632a, 
    Example@5051613a, 
    Example@6212cffd
    ] 

创建了三个单独的对象。

我想要的输出是:

 ExampleList=[
    Example@1eda78db, 
    Example@1eda78db, 
    Example@68a94d19
    ] 

创建了 2 个对象(1eda78db 和 68a94d19)而不是 3 个。

我看到了一个类似的帖子,它需要实现readResolve(),但是有没有什么简单的方法可以做到这一点,它的内存更少? 如何将两个 Java 序列化对象重新链接在一起?

这是我要解决的用例。对象accountTobjId0inaccountNameinfoTList是一样的。我想修改对象accountT并希望看到它反映在objId0

{
  "accountT": {
    "poidId0": 200,
    "poidDb": 1,
    "poidType": "/account",
    "poidRev": 10
  },
  "accountNameinfoTList": [
    {
      "id": {
        "recId": 1
      },
      "objId0": {
        "poidId0": 200,
        "poidDb": 1,
        "poidType": "/account",
        "poidRev": 10
      },
      "address": "2632 Marine Way",
      "canonCompany": "child corp",
      "canonCountry": "GB",
      "city": "Elstree",
      "company": "Child Corp"
    }
  ]
}

标签: javaspring-mvcserializationjacksondeserialization

解决方案


不确定这是否可以帮助其他人。我能够通过利用@JsonCreator

我创建了一个静态方法,createExample通过使用传递值@ JsonProperty

public class Example implements Serializable {
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @JsonValue
    private String a;
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @JsonValue
    private String b;
    public Example() {
    }
    @JsonCreator
    public static Example createExample(@JsonProperty("a") String a,
                                        @JsonProperty("b") String b) {
        Example example = new Example();
        example.a = a;
        example.b = b;
        return (Example) ReferenceMap.findOriginalObject(example);
    }
}

使用 ReferenceMap 保存 WeakHashMap 中的详细信息!

public class ReferenceMap {
    public static final String KEY_WEAK_REFERENCE_MAP = "weakReferenceMap";

    public static Object findOriginalObject(Object object) {
        Map<Object, Reference<Object>> weakReferenceMap = fetchWeakReferenceMapFromServletRequest();
        WeakReference<Object> originalReference = (WeakReference<Object>) weakReferenceMap.get(object);
        Object originalObject = originalReference == null ? null : originalReference.get();
        if (originalObject == null) {
            originalObject = object;
            weakReferenceMap.put(originalObject, new WeakReference<>(originalObject));
        }
        return originalObject;
    }

    private static Map<Object, Reference<Object>> fetchWeakReferenceMapFromServletRequest() {
        HttpServletRequest
                request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        Object requestAttribute = request.getAttribute(KEY_WEAK_REFERENCE_MAP);
        if (requestAttribute == null) {
            Map<Object, Reference<Object>> weakReferenceMap = new WeakHashMap<>();
            requestAttribute = weakReferenceMap;
            request.setAttribute(KEY_WEAK_REFERENCE_MAP, weakReferenceMap);
        }
        return (Map<Object, Reference<Object>>) requestAttribute;
    }
}

有了这个,我确信总是得到一个现有的实例而不是创建一个新的实例。

更新 将 WeakHashMap 保存在 ServletRequestAttributes 而不是 JVM 的堆中。


推荐阅读