您可以使用自定义序列化程序来构建动态 JSON 响应。
但是您应该以某种方式将动态类别名称传递给此序列化程序。
在我的示例中,我将其存储为实体成员 - 私有字符串实例。 (如果您使用 JPA 实体,请使用 @Transient 注释以不将此字段映射到 DB 结构)
package com.example.dto.search;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.util.ArrayList;
@JsonSerialize(using = BucketSerializer.class)
public class Bucket {
private int docCount;
private String key;
// can be more specific if you have some superclass on top of all subcategories
private List<Object> subCategoryElements = new ArrayList<>();
private String nameOfSubcategory;
// getters
}
和序列化器类:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.util.Optional;
public class BucketSerializer extends StdSerializer<Bucket> {
public BucketSerializer() {
this(null);
}
public BucketSerializer(Class<Bucket> t) {
super(t);
}
@Override
public void serialize(Bucket bucket, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeNumberField("docCount", bucket.getDocCount());
gen.writeStringField("key", bucket.getKey();
gen.writeObjectField(Optional.ofNullable(bucket.getNameOfSubcategory()).orElse("unnamedCategory"),
bucket.getSubCategoryElements());
gen.writeEndObject();
}
}
Maven 依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.8</version>
</dependency>
_____EDIT_1_____
我复制了您的案例并提出了一些建议。
发布我如何解决这个问题的代码:
型号:
// Aggregation
public class Aggregation {
private Categories categories;
public Categories getCategories() {
return categories;
}
public void setCategories(Categories categories) {
this.categories = categories;
}
}
// Cetagories
import java.util.ArrayList;
import java.util.List;
public class Categories {
private List<Bucket> buckets = new ArrayList<>();
private int docCountErrorUpperBound;
private int sumOtherDocCount;
public List<Bucket> getBuckets() {
return buckets;
}
public void setBuckets(List<Bucket> buckets) {
this.buckets = buckets;
}
public int getDocCountErrorUpperBound() {
return docCountErrorUpperBound;
}
public void setDocCountErrorUpperBound(int docCountErrorUpperBound) {
this.docCountErrorUpperBound = docCountErrorUpperBound;
}
public int getSumOtherDocCount() {
return sumOtherDocCount;
}
public void setSumOtherDocCount(int sumOtherDocCount) {
this.sumOtherDocCount = sumOtherDocCount;
}
}
//Bucket
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonDeserialize(using = BucketDeserializer.class)
public class Bucket {
private int docCount;
private String key;
private Categories subCategories;
public int getDocCount() {
return docCount;
}
public void setDocCount(int docCount) {
this.docCount = docCount;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Categories getSubCategories() {
return subCategories;
}
public void setSubCategories(Categories subCategories) {
this.subCategories = subCategories;
}
}
反序列化器:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class BucketDeserializer extends StdDeserializer<Bucket> {
public static final String DOC_COUNT = "doc_count";
public static final String KEY = "key";
public static final List<String> knownFieldNames = Arrays.asList(DOC_COUNT, KEY);
public BucketDeserializer() {
this(null);
}
public BucketDeserializer(Class<Bucket> c) {
super(c);
}
@Override
public Bucket deserialize(JsonParser jsonParser, DeserializationContext desContext) throws IOException {
Bucket bucket = new Bucket();
JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
ObjectMapper objectMapper = new ObjectMapper();
bucket.setDocCount(jsonNode.get(DOC_COUNT).asInt());
bucket.setKey(jsonNode.get(KEY).asText());
String unknownField = getUnknownField(jsonNode.fieldNames());
if (unknownField != null)
bucket.setSubCategories(objectMapper.convertValue(jsonNode.get(unknownField), Categories.class));
return bucket;
}
public String getUnknownField(Iterator<String> fieldNames) {
while (fieldNames.hasNext()) {
String next = fieldNames.next();
if (!knownFieldNames.contains(next))
return next;
}
return null;
}
}
主要思想是找到未知/动态字段/json键。
从 JsonNode 你可以得到所有的字段名。我解决了声明所有已知字段名称的问题,然后找到不属于此列表成员的字段。您也可以使用 switch 来通过字段名称调用 setter 或创建另一个映射器。你也可以看看org.json.JSONObject类,它可以通过索引号检索值。
你不必关心嵌套的桶,因为这个反序列化器也会处理它们。
这是我使用的 JSON 请求正文:
{
"categories": {
"buckets": [
{
"doc_count": 12,
"key": "IT",
"it_category": {
"buckets": [
{
"doc_count": 12,
"key": "Programming"
}
],
"docCountErrorUpperBound": 0,
"sumOtherDocCount": 0
}
},
{
"doc_count": 1,
"key": "Handy Man",
"plumb_category": {
"buckets": [
{
"doc_count": 1,
"key": "Plumbing"
}
],
"docCountErrorUpperBound": 0,
"sumOtherDocCount": 0
}
}
],
"docCountErrorUpperBound": 0,
"sumOtherDocCount": 0
}
}
这是我得到的回应:
{
"categories": {
"buckets": [
{
"docCount": 12,
"key": "IT",
"subCategories": {
"buckets": [
{
"docCount": 12,
"key": "Programming",
"subCategories": null
}
],
"docCountErrorUpperBound": 0,
"sumOtherDocCount": 0
}
},
{
"docCount": 1,
"key": "Handy Man",
"subCategories": {
"buckets": [
{
"docCount": 1,
"key": "Plumbing",
"subCategories": null
}
],
"docCountErrorUpperBound": 0,
"sumOtherDocCount": 0
}
}
],
"docCountErrorUpperBound": 0,
"sumOtherDocCount": 0
}
}
响应使用标准名称进行序列化,因为我没有使用任何自定义序列化程序。您也可以使用我在原始帖子中提出的自定义序列化程序对其进行自定义。