使用GSON将JSON转换为Java对象时,如何覆盖Java映射? [英] How do I override a Java map when converting a JSON to Java Object using GSON?
问题描述
我有一个如下所示的JSON字符串:
I have a JSON string which looks like this:
{
"status": "status",
"date": "01/10/2019",
"alerts": {
"labels": {
"field1": "value1",
"field2": "value2",
"field3": "value3",
"field100": "value100"
},
"otherInfo" : "other stuff"
},
"description": "some description"
}
我对应的Java类如下:
My corresponding Java classes look like the following:
public class Status {
private String status;
private String date;
private Alerts alerts;
private String description;
}
还有
public class Alerts {
private Map<String, String> labels;
private String otherInfo;
public Map<String, String> getLabels() {
return labels();
}
}
我正在使用以下方法将给定的JSON解析为Java对象:
I'm parsing the given JSON into Java object using this:
Status status = gson.fromJson(statusJSONString, Status.class);
这也为我提供了Status类的Alerts对象:
This also gives me Alerts object from Status class:
Alerts alerts = status.getAlerts();
这是我的问题:
让我们考虑labels
:
我想使label
映射中的键不区分大小写.因此,例如,如果提供的键/值对是"field1" : "value1"
或"Field1" : "value1"
或"fIeLD1":"value1"
,我希望能够通过简单地调用alerts.getLabels.get("field1")
来检索它们.
I want to make keys in the label
map the case-insensitive. So for example, if the provided key/value pair is "field1" : "value1"
, or "Field1" : "value1"
or "fIeLD1":"value1"
, I want to be able to retrieve them by simply calling alerts.getLabels.get("field1")
.
理想情况下,我想在最初创建labels
映射时将键设置为小写.我查看了Gson反序列化示例,但不清楚如何实现这一目标.
Ideally, I want to set the keys to be lowercase when the labels
map is originally created. I looked into Gson deserialization examples, but I'm not clear exactly how to approach this.
推荐答案
您可以编写自己的MapTypeAdapterFactory
,从而始终使用降低的键来创建Map
.我们的适配器将基于com.google.gson.internal.bind.MapTypeAdapterFactory
.我们不能扩展它,因为它是final
,但是我们的Map
非常简单,所以我们只复制重要的代码:
You can write your own MapTypeAdapterFactory
which creates Map
always with lowered keys. Our adapter will be based on com.google.gson.internal.bind.MapTypeAdapterFactory
. We can not extend it because it is final
but our Map
is very simple so let's copy only important code:
class LowercaseMapTypeAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
TypeAdapter<String> stringAdapter = gson.getAdapter(TypeToken.get(String.class));
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) { }
@Override
public T read(JsonReader in) throws IOException {
JsonToken peek = in.peek();
if (peek == JsonToken.NULL) {
in.nextNull();
return null;
}
Map<String, String> map = new HashMap<>();
in.beginObject();
while (in.hasNext()) {
JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
String key = stringAdapter.read(in).toLowerCase();
String value = stringAdapter.read(in);
String replaced = map.put(key, value);
if (replaced != null) {
throw new JsonSyntaxException("duplicate key: " + key);
}
}
in.endObject();
return (T) map;
}
};
}
}
现在,我们需要通知我们Map
应该使用我们的适配器反序列化:
Now, we need to inform that our Map
should be deserialised with our adapter:
class Alerts {
@JsonAdapter(value = LowercaseMapTypeAdapterFactory.class)
private Map<String, String> labels;
private String otherInfo;
// getters, setters, toString
}
假设我们的JSON
payload
如下所示:
Assume that our JSON
payload
looks like below:
{
"status": "status",
"date": "01/10/2019",
"alerts": {
"labels": {
"Field1": "value1",
"fIEld2": "value2",
"fielD3": "value3",
"FIELD100": "value100"
},
"otherInfo": "other stuff"
},
"description": "some description"
}
示例用法:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.internal.JsonReaderInternalAccess;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class GsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
Gson gson = new GsonBuilder().create();
Status status = gson.fromJson(new FileReader(jsonFile), Status.class);
System.out.println(status.getAlerts());
}
}
上面的代码显示:
Alerts{labels={field1=value1, field100=value100, field3=value3, field2=value2}, otherInfo='other stuff'}
这确实是棘手的解决方案,应谨慎使用.不要将此适配器与非常复杂的Map
-es一起使用.另一方面,OOP
更喜欢简单的解决方案.例如,为Map
创建decorator
,如下所示:
This is really tricky solution and it should be used carefully. Do not use this adapter with much complex Map
-es. From other side, OOP
prefers much simple solutions. For example, create decorator
for a Map
like below:
class Labels {
private final Map<String, String> map;
public Labels(Map<String, String> map) {
Objects.requireNonNull(map);
this.map = new HashMap<>();
map.forEach((k, v) -> this.map.put(k.toLowerCase(), v));
}
public String getValue(String label) {
return this.map.get(label.toLowerCase());
}
// toString
}
向Alerts
类添加新方法:
public Map<String, String> toLabels() {
return new Labels(labels);
}
示例用法:
status.getAlerts().toLabels()
这将为您提供非常灵活和安全的行为.
Which gives you a very flexible and secure behaviour.
这篇关于使用GSON将JSON转换为Java对象时,如何覆盖Java映射?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!