当前位置: 首页 > 知识库问答 >
问题:

如何在Jackson中打开列表包装项目的列表?

萧树
2023-03-14

我有一个豆子类似于:

public class Product {

    public String id;
    public String vendor;
    public Set<Image> images;
}

public class Image {

    public String originalSrc;
}

我正在尝试反序列化类似于以下内容的JSON:

{
  "id": "gid:\/\/mysite\/Product\/1853361520730",
  "vendor": "gadgetdown",
  "images": {
    "edges": [
      {
        "node": {
          "originalSrc": "https:\/\/cdn.something.com"
        }
      },
      {
        "node": {
          "originalSrc": "https:\/\/cdn.something.com"
        }
      }
    ]
  }

我无法反序列化该对象,因为每个图像对象都包装在节点对象中,并集中在边对象中。

编辑:为了清楚起见,我不想通过使用bean来完成这一点,这个示例是一个简化,JSON有效负载中的所有数组项都包装在这个边缘节点表示中。

共有3个答案

卜和悌
2023-03-14

要将图像从节点{“originalSrc”:“https:\/\/cdn.something.com”}中展开只需使用注释

@JsonRootName(value = "node")
class Image {
    public String originalSrc;
}

但将图像集合从<代码>“图像”:{“边”:[{…}中展开,{...}] } 有点复杂,需要使用自定义的JsonDeserializer

class Product {
    public String id;
    public String vendor;

    @JsonDeserialize(using = ImageSetDeserializer.class)
    public Set<Image> images;
}


class ImageSetDeserializer extends JsonDeserializer<Set<Image>> {
    public Set<Image> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
        JsonNode node = mapper.readTree(jsonParser);
        return mapper.convertValue(node.get("edges").findValues("node"), new TypeReference<Set<Image>>() {});
    }
}

最后一个测试:

public class ProductTest {

    private final String source = "{\n" +
            "  \"id\": \"gid:\\/\\/mysite\\/Product\\/1853361520730\",\n" +
            "  \"vendor\": \"gadgetdown\",\n" +
            "  \"images\": {\n" +
            "    \"edges\": [\n" +
            "      {\n" +
            "        \"node\": {\n" +
            "          \"originalSrc\": \"https:\\/\\/cdn.something.com\"\n" +
            "        }\n" +
            "      },\n" +
            "      {\n" +
            "        \"node\": {\n" +
            "          \"originalSrc\": \"https:\\/\\/cdn.something.com\"\n" +
            "        }\n" +
            "      }\n" +
            "    ]\n" +
            "  }" +
            "}";

    @Test
    public void test() throws IOException {
        ObjectMapper mapper = new ObjectMapper();

        Product product = mapper.readValue(source, Product.class);

        assertEquals(product.id, "gid://mysite/Product/1853361520730");
        assertEquals(product.vendor, "gadgetdown");
        assertNotNull(product.images);
        List<Image> images = new ArrayList<>(product.images);
        assertEquals(images.size(), 2);
        assertEquals(images.get(0).originalSrc, "https://cdn.something.com");
        assertEquals(images.get(1).originalSrc, "https://cdn.something.com");
    }
}
贾俊艾
2023-03-14

如果每个列表都有如下结构:

{
  "images": {
    "edges": [
      {
        "node": {
          "entry": "entry-value"
        }
      }
    ]
  }
}

每个列表都是一个具有边属性的JSON对象,数组中的每个元素都由节点属性的JSON对象包装。对于这种结构,我们可以编写类似于Jackson的通用反序列化器-将对象的内部列表反序列化为一个更高级别问题的列表。

示例设置反序列器:

class InnerSetDeserializer extends JsonDeserializer<Set> implements ContextualDeserializer {

  private final JavaType propertyType;

  public InnerSetDeserializer() {
    this(null);
  }

  public InnerSetDeserializer(JavaType propertyType) {
    this.propertyType = propertyType;
  }

  @Override
  public Set deserialize(JsonParser p, DeserializationContext context) throws IOException {
    p.nextToken(); // SKIP START_OBJECT
    p.nextToken(); // SKIP any FIELD_NAME

    CollectionType collectionType = getCollectionType(context);
    List<Map<String, Object>> list = context.readValue(p, collectionType);

    p.nextToken(); // SKIP END_OBJECT

    return list.stream().map(Map::values).flatMap(Collection::stream).collect(Collectors.toSet());
  }

  private CollectionType getCollectionType(DeserializationContext context) {
    TypeFactory typeFactory = context.getTypeFactory();
    MapType mapType =
        typeFactory.constructMapType(
            Map.class, String.class, propertyType.getContentType().getRawClass());

    return typeFactory.constructCollectionType(List.class, mapType);
  }

  @Override
  public JsonDeserializer<?> createContextual(DeserializationContext context, BeanProperty property) {
    return new InnerSetDeserializer(property.getType());
  }
}

我们可以使用如下:

class Product {

  private String id;
  private String vendor;

  @JsonDeserialize(using = InnerSetDeserializer.class)
  private Set<Image> images;

  // getters, setters
}

示例应用程序:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class JsonApp {

  public static void main(String[] args) throws IOException {
    File jsonFile = new File("./resources/test.json");

    ObjectMapper mapper = new ObjectMapper();
    Product product = mapper.readValue(jsonFile, Product.class);
    System.out.println(product);
  }
}

以上代码打印:

Product{id='gid://mysite/Product/1853361520730', vendor='gadgetdown', images=[Image{originalSrc='https://cdn.something.com'}, Image{originalSrc='https://cdn.something.com'}]}
戚飞
2023-03-14

因此,图像不是一个集合,而是一个包含边缘列表的对象

public class Images {

   private List<Edge> edges;

    }

每个边都包含节点对象,

public class Edge {

   private Node node;

   }

每个节点都有单个字符串属性

public class Node  {

 private String originalSrc;

  }
 类似资料:
  • 每一行中的项目将根据其文本大小而有所不同。Flatlist用于呈现项目。 TagView.js 风格 结果 但这里的项目没有用设备宽度包装。有没有包装内容物?

  • 问题内容: 有时,使用熊猫时,我会得到一系列元组/列表。例如,在进行分组并传递具有多个返回值的函数时,这很常见: 什么是“解包”此结构的正确方法,以便获得具有两列的DataFrame? 一个相关的问题是如何将这个结构或生成的数据框解包到两个Series / array对象中。这几乎可以工作: 但这是 而且需要采取额外的措施来挤压它。 问题答案: 也许这是最愚蠢的(我猜是最pythonic): 如果

  • 我的目标是从用户输入的“AM”-“PM”字符串格式打印包含24小时十进制格式的进入和退出时间的列表,如以下字符串数组:{6AM#8AM,11AM#1PM,7AM#8PM,7AM#8AM,10AM#12PM,12PM#4PM,1PM#4PM,8AM#9AM} 我在for循环中声明了各个列表,并在循环中为它们赋值,但从代码中得到了以下运行时异常:java。lang.IndexOutOfBoundsEx

  • 问题内容: 这个问题已经在这里有了答案 : 循环“忘记”以删除一些项目[重复] (10个答案) 6年前关闭。 我想从列表“ a”中删除项目,其中列表“ b”包含在列表“ a”中找到带有单词的项目 结果应为: 这是因为在列表“ a”的项目中找到了单词“二”和“五六”。 这是我尝试解决的方法: 返回: 为什么这不起作用,如何解决此问题? 谢谢。 问题答案: 遍历列表时,请勿修改列表。这样做会产生不良的

  • 问题内容: 我有一个清单清单: 搜索列表并返回其中一项的位置的最干净方法是什么? 我已经看过该方法,但是似乎并没有在列表中解压缩列表。 给出:,不是我所期望的… 问题答案: 我会做这样的事情: 这将返回一个元组列表,其中第一个索引是第一个列表中的位置,第二个索引是第二个列表中的位置(注意:您要查找的颜色,即)。 对于问题中的示例,返回值为: 如果您只需要懒惰地找到找到颜色的第一个位置,则可以使用以

  • 问题内容: 我正在从表单文本字段,并从复选框中的字段 我将它们像这样组合: (此函数在列表中的字符串内去除空格。) 但在这种情况下是空的(没有新的标签进入),但也有一些,包含一个空字符串。 例如,来自: 我如何摆脱空字符串? 如果列表中有一个空字符串: 但是,如果没有空字符串: 但这给出了: 为什么会发生这种情况,我该如何解决? 问题答案: 1)几乎是英式风格: 使用操作员测试是否存在,然后应用该