Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在使用GSON将JSON转换为Java对象时,如何覆盖Java映射?_Java_Android_Json_Gson - Fatal编程技术网

在使用GSON将JSON转换为Java对象时,如何覆盖Java映射?

在使用GSON将JSON转换为Java对象时,如何覆盖Java映射?,java,android,json,gson,Java,Android,Json,Gson,我有一个JSON字符串,如下所示: { "status": "status", "date": "01/10/2019", "alerts": { "labels": { "field1": "value1", "field2": "value2", "field3": "value3", "field100": "value100" }, "otherInfo" : "other stu

我有一个JSON字符串,如下所示:

{
 "status": "status",
 "date": "01/10/2019",
 "alerts": {
     "labels": {
         "field1": "value1",
         "field2": "value2",
         "field3": "value3",
         "field100": "value100"
     },
     "otherInfo" : "other stuff"
 },
 "description": "some description"
}
我对应的Java类如下所示:

public class Status {
    private String status;
    private String date;
    private Alerts alerts;
    private String description;
}

这还为我提供了来自状态类的警报对象:

Alerts alerts = status.getAlerts();
我的问题是:

我们考虑标签:

我想使标签映射中的键不区分大小写。例如,如果提供的键/值对是field1:value1,或field1:value1或field1:value1,我希望能够通过调用alerts.getLabels.getfield1来检索它们


理想情况下,我希望在最初创建标签映射时将键设置为小写。我研究了Gson反序列化示例,但我不清楚具体如何实现这一点。

这里没有什么可以做的。即使扩展了HashMap,问题是当JSON被反序列化时,它不会调用本机方法。您可以执行以下操作,但这相当麻烦:

import java.util.HashMap;

public class HashMapCaseInsensitive extends HashMap<String, String> {

    private boolean convertedToLower = false;

    @Override
    public String put(String key, String value) {
        if(!convertedToLower){
            convertToLower();
        }
        return super.put(key.toLowerCase(), value);
    }

    @Override
    public String get(Object key) {
        if(!convertedToLower){
            convertToLower();
        }
        return super.get(key.toString().toLowerCase());
    }

    private void convertToLower(){
        for(String key : this.keySet()){
            String data = this.get(key);
            this.remove(key);
            this.put(key.toLowerCase(), data);
        }
        convertedToLower = true;
    }

}

虽然这不是一个非常通用的解决方案,但是,我认为这将符合您的目的

我建议您为Gson创建一个适配器,它可以为您转换映射值。适配器可能如下所示

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

import java.lang.reflect.Type;

final class GSONAdapter implements JsonDeserializer<String> {

    private static final GSONAdapter instance = new GSONAdapter();

    static GSONAdapter instance() {
        return instance;
    }

    @Override
    public String deserialize(JsonElement jsonElement, Type type,
                              JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {

        // Here I am taking the elements which are starting with field
        // and then returning the lowercase version
        // so that the labels map is created this way
        if (jsonElement.getAsString().toLowerCase().startsWith("field"))
            return jsonElement.getAsString().toLowerCase();
        else return jsonElement.getAsString();
    }
}
希望这能解决你的问题

您可以编写自己的MapTypeAdapterFactory,它始终使用较低的键创建地图。我们的适配器将基于com.google.gson.internal.bind.MapTypeAdapterFactory。我们不能扩展它,因为它是最终的,但我们的映射非常简单,所以让我们只复制重要的代码:

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;
            }
        };
    }
}
假设我们的JSON负载如下所示:

{
  "status": "status",
  "date": "01/10/2019",
  "alerts": {
    "labels": {
      "Field1": "value1",
      "fIEld2": "value2",
      "fielD3": "value3",
      "FIELD100": "value100"
    },
    "otherInfo": "other stuff"
  },
  "description": "some description"
}
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
}
用法示例:

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());
    }
}
status.getAlerts().toLabels()
以上代码打印:

Alerts{labels={field1=value1, field100=value100, field3=value3, field2=value2}, otherInfo='other stuff'}
这是一个非常棘手的解决方案,应该谨慎使用。不要将此适配器用于太复杂的映射。从另一方面来说,OOP更喜欢简单的解决方案。例如,为地图创建装饰器,如下所示:

{
  "status": "status",
  "date": "01/10/2019",
  "alerts": {
    "labels": {
      "Field1": "value1",
      "fIEld2": "value2",
      "fielD3": "value3",
      "FIELD100": "value100"
    },
    "otherInfo": "other stuff"
  },
  "description": "some description"
}
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
}
用法示例:

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());
    }
}
status.getAlerts().toLabels()

这为您提供了非常灵活和安全的行为。

我运行了该代码,并且警报为空。您在提取警报类时做了什么特别的事情吗?我知道了,变量名应该是label而不是labelMap。我已经编辑了你的问题。谢谢@reazmurshed你能检查下面的答案吗?在我的本地环境中,有足够数量的代码来复制问题,问题清晰,值得称赞:@我知道你们已经接受了答案,但看看我的答案。也许它会对您有所帮助。通常,扩展内部集合实现是一种不好的做法。更好的方法是创建装饰器。在构造函数中调用convertToLower方法也更好。@MichałZiober问题是在这种情况下,由于反序列化,构造函数不会被调用。但我同意。我知道这是一个非常特殊的情况,但无论如何,我们应该尽量避免这种情况。此外,它还能工作。很高兴知道。
status.getAlerts().toLabels()