【问题标题】:ObjectMapper creates list from fields with specific nameObjectMapper 从具有特定名称的字段创建列表
【发布时间】:2017-10-04 10:02:09
【问题描述】:

我有一个 JSON,我将其解析为一个 Result 对象列表。此对象包含 5 个字符串字段,称为 photo1、photo2 等(基于 JSON)是否可以将它们直接读入 List 字段?

JSON 喜欢:

{
    "ErrorMessage": null,
    "Result": [{
        "id": "462290",
        "name_English": "name in english",
        "name_Local": "külföldiül a név",
        "zipcode": "5463",
        "photo1": "dfglkj.com/blabla",
        "photo2": "dfglkj.com/blabla",
        "photo3": "dfglkj.com/blabla",
        "photo4": "dfglkj.com/blabla",
        "photo5": "dfglkj.com/blabla"
    }]
}

还有我的对象:

static final class ApiResponse
    {
        public String ErrorMessage;
        public List<Result> Result = new ArrayList<Result>();
    }

 static final class Result
    {
        public String id;
        public String name_English;
        public String name_Local;
        public List<String> photos;
        public String zipcode;
    }

我有一个 ObjectMapper:

private static ObjectMapper newObjectMapper()
        {
            final ObjectMapper om =
                new ObjectMapper()  //
                .configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false)  //
                .configure(JsonParser.Feature.CANONICALIZE_FIELD_NAMES, true);

            om.registerSubtypes(ApiResponse.class);

            return om;
        }

在解析器中:

final ApiResponse ret = OM.readValue(inputStream, ApiResponse.class);

【问题讨论】:

  • 请添加一个示例 json 和你想从中获取的对象,不清楚你在问什么。
  • 我更新了问题
  • 你做了什么吗?我们可以看看吗?
  • photo1,photo2,... 将是有限的或不固定的
  • 你可以用@JsonAnySetter stackoverflow.com/a/30894845/1398418 来做我认为没有更简单的方法。

标签: java json jackson


【解决方案1】:

根据 Gaetano 的回答,我写了一个更通用的解决方案(任意数量的照片,任意数量的其他字段

@Override public List<Result> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException
        {
            JsonNode node = jp.getCodec().readTree(jp);
            List<Result> listResult = new ArrayList<Result>();

            for (JsonNode interNode : node)
            {
                Result result = new Result();

                for (int i = 1; i < 30; i++)
                {
                    if (interNode.get("photo" + i) != null)
                    {
                        result.photos.add(interNode.get("photo" + i).getTextValue());
                    }
                    else
                    {
                        break;
                    }
                }

                for (Field field : result.getClass().getDeclaredFields())
                {
                    if (interNode.get(field.getName()) != null)
                    {
                        try
                        {
                            field.set(result, interNode.get(field.getName()).getTextValue());
                        }
                        catch (IllegalAccessException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }

                listResult.add(result);
            }

            return listResult;
        }

【讨论】:

【解决方案2】:

我发现的最佳解决方案是为您的 ObjectMapper 定义一个 Custom Deserializer

您可以告诉 JACKSON 使用自定义反序列化器,并对 ApiResponse 类上的“结果”对象列表进行特定注释:

public class ApiResponse {

    private String errorMessage;

    @JsonDeserialize(using = CustomDeserializer.class)
    private List<Result> result = new ArrayList<Result>();

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String arg) {
        errorMessage = arg;
    }

    public List<Result> getResult() {
        if(result==null)
        {
            result = new ArrayList<Result>();
        }
        return result;
    }

    public void setResult(List<Result> arg) {
        result = arg;
    }
}

public class Result {
     private String id;
     private String name_English;
     private String name_Local;
     private List<String> photos;
     private String zipcode;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName_English() {
        return name_English;
    }
    public void setName_English(String name_English) {
        this.name_English = name_English;
    }
    public String getName_Local() {
        return name_Local;
    }
    public void setName_Local(String name_Local) {
        this.name_Local = name_Local;
    }
    public List<String> getPhotos() {
        if(photos == null)
        {
            photos = new ArrayList<String>();
        }
        return photos;
    }
    public void setPhotos(List<String> photos) {
        this.photos = photos;
    }
    public String getZipcode() {
        return zipcode;
    }
    public void setZipcode(String zipcode) {
        this.zipcode = zipcode;
    }
}

请注意java命名约定:https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html

自定义反序列化器将如下所示:

public class CustomDeserializer extends StdDeserializer<List<Result>> {

private static final long serialVersionUID = -3483096770025118080L;

public CustomDeserializer() {
    this(null);
}

public CustomDeserializer(Class<?> vc) {
    super(vc);
}

@Override
public List<Result> deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
    JsonNode node = jp.getCodec().readTree(jp);

    List<Result> listResult = new ArrayList<Result>();
    for (JsonNode interNode : node) {

        Result result = new Result();           

        if (interNode.get("id") != null) {
            result.setId(interNode.get("id").asText());
        }
        if (interNode.get("name_English") != null) {
            result.setName_English(interNode.get("name_English").asText());
        }
        if (interNode.get("name_Local")!= null) {
            result.setName_Local(interNode.get("name_Local").asText());
        }
        if (interNode.get("zipcode") !=null ) {
            result.setZipcode(interNode.get("zipcode").asText());
        }

        // photo array
        if (interNode.get("photo1") != null) {
            result.getPhotos().add(interNode.get("photo1").asText());
        }
        if (interNode.get("photo2") != null) {
            result.getPhotos().add(interNode.get("photo2").asText());
        }
        if (interNode.get("photo3") != null) {
            result.getPhotos().add(interNode.get("photo3").asText());
        }
        if (interNode.get("photo4") != null) {
            result.getPhotos().add(interNode.get("photo4").asText());
        }
        if (interNode.get("photo5") != null) {
            result.getPhotos().add(interNode.get("photo5").asText());
        }

        listResult.add(result);
    }

    return listResult;
}

【讨论】:

    【解决方案3】:

    如果您可以更改 POJO 代码,那么最简单的选择是使用 @JsonAnySetter@JsonAnyGetter 并在那里编写自定义代码。(如建议的 @Oleg)但不要忘记 @JsonIgnore List&lt;String&gt; photos

    @Data // lombok.Data; for getters and setters
    public static final class Result {
        public String id;
        public String name_English;
        public String name_Local;
        @JsonIgnore
        public List<String> photos = new ArrayList<>();
        public String zipcode;
    
        @JsonAnySetter
        public void setOther(String key, String value){
            photos.add(value);
        }
        @JsonAnyGetter
        public Map<String,String> getOther(){
            Map<String,String> map = new HashMap<>();
            for (int i = 0;i<photos.size();i++)
                map.put("photo" + i + 1, photos.get(i));
            return map;
        }
    }
    

    但是如果你不能改变 POJO 代码,你将不得不编写自定义的序列化器和反序列化器

    【讨论】:

    • 这样,任何其他属性都将进入“照片”列表,而不仅仅是名为“photos1”“photos2”等的属性。
    • OP 的 json 没有其他属性,所以这不是问题。这可以通过检查keysetOther 中以photo 开头来变得更加健壮。 @Gaetano
    • 是的。没想到。
    猜你喜欢
    • 1970-01-01
    • 2022-10-12
    • 2022-07-25
    • 2015-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多