首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap不能强制转换为com.google.gson.internal.LinkedTreeMap

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap不能强制转换为com.google.gson.internal.LinkedTreeMap
EN

Stack Overflow用户
提问于 2021-04-15 16:57:54
回答 1查看 460关注 0票数 0

我正在尝试将一个json解析成java对象。在这样做的时候,我得到了一个异常"java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap不能被转换为com.google.gson.internal.LinkedTreeMap“。不知何故,有时这段代码运行得很好。以下是代码。

代码语言:javascript
复制
import java.util.List;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.internal.LinkedTreeMap;

public class Preference3 {
    
    public static void main(String[] args) {
        String jsonString1 = "{\"ca\":{\"industry\":[{\"path\":\"/abc/global/choice/in/financial\",\"type\":\"checked\"},{\"path\":\"/abc/global/choice/in/asset-management\",\"type\":\"checked\"}]},\"fr\":{\"country\":[{\"path\":\"/abc/global/choice/in/country/europe/italy\",\"type\":\"checked\"},{\"path\":\"/abc/global/choice/in/country/europe/switzerland\",\"type\":\"checked\"},{\"path\":\"/abc/global/choice/in/country/europe/sweden\",\"type\":\"checked\"}],\"services\":[{\"path\":\"/abc/global/choice/in/technology\",\"type\":\"checked\"},{\"path\":\"/abc/global/choice/in/technology/technology-media\",\"type\":\"checked\"},{\"path\":\"/abc/global/choice/in/technology/media\",\"type\":\"checked\"},{\"path\":\"/abc/global/choice/in/technology/telecommunications\",\"type\":\"checked\"}]}}";
        
        Map<String, LinkedTreeMap<String, List>> map = new Gson().fromJson(jsonString1, LinkedTreeMap.class);
        for (Map.Entry<String, LinkedTreeMap<String, List>> letterEntry : map.entrySet()) {
            String territory = letterEntry.getKey();
        
        for (Map.Entry<String, List> nameEntry : letterEntry.getValue().entrySet()) {
            String preferenceType = nameEntry.getKey();
            JsonArray preferenceElements = (JsonArray) new Gson().toJsonTree(nameEntry.getValue());
            for (JsonElement preferenceElement : preferenceElements) {
                JsonObject preferenceObj = preferenceElement.getAsJsonObject();
                String preferencePath = preferenceObj.get("path").getAsString();
                    System.out.println(preferencePath);
            }
            }
        }
    }}

有没有其他更好的方法来解析这个json。我浏览了一些博客,但没有找到任何其他解决方案,也不能直接将其改编为任何pojo。热心的帮助

EN

回答 1

Stack Overflow用户

发布于 2021-04-15 20:10:19

我不知道为什么会出现异常(不是有勘误表吗?--我想知道为什么在使用相同的类加载器时A类不能转换为A类)。另外,请不要将反序列化(映射到映射或普通对象)和JsonElement子类混为一谈。如果您这样做,那么您必须使用适当的类型,并告诉Gson使用它们。我还强烈建议不要坚持使用具体的类,特别是如果它们位于内部包中--它们可能会在任何完全破坏您的代码的发行版中发生变化。坚持使用接口。

有没有其他更好的方法来解析这个json。

既是也不是。Gson更像是一个JSON解析/编写和序列化/反序列化。如果问题接近于通常在Gson中是如何完成的,那么您所描述的问题只是不是一个JSON查询库。

我浏览了一些博客,但没有找到任何其他解决方案,也不能直接将其改编为任何pojo。

你必须使用地图,除非你知道所有的关键字,并确保你可以用DTO描述它们。

以下是查询path字符串的一些更好的方法:

代码语言:javascript
复制
private static final Iterable<String> expected = ImmutableList.of(
        "/abc/global/choice/in/financial",
        "/abc/global/choice/in/asset-management",
        "/abc/global/choice/in/country/europe/italy",
        "/abc/global/choice/in/country/europe/switzerland",
        "/abc/global/choice/in/country/europe/sweden",
        "/abc/global/choice/in/technology",
        "/abc/global/choice/in/technology/technology-media",
        "/abc/global/choice/in/technology/media",
        "/abc/global/choice/in/technology/telecommunications"
);

可以通过JsonParser类将整个JSON文档加载到内存中。In不使用反序列化和映射straregies,因此它可能是使用Gson JsonElement及其子类表示任何JSON元素的最快、最健壮和最本机的方法。请注意,此处未显示反序列化和Map

代码语言:javascript
复制
@Test
public void testWithJsonTree()
        throws IOException {
    try ( final JsonReader jsonReader = new JsonReader(new InputStreamReader(openJsonInputStream())) ) {
        final JsonElement jsonTree = JsonParser.parseReader(jsonReader);
        final Collection<String> actual = new ArrayList<>();
        for ( final Map.Entry<String, JsonElement> letterEntry : jsonTree.getAsJsonObject().entrySet() ) {
            for ( final Map.Entry<String, JsonElement> nameEntry : letterEntry.getValue().getAsJsonObject().entrySet() ) {
                for ( final JsonElement preferenceElement : nameEntry.getValue().getAsJsonArray() ) {
                    final String preferencePath = preferenceElement.getAsJsonObject().get("path").getAsString();
                    actual.add(preferencePath);
                }
            }
        }
        Assertions.assertIterableEquals(expected, actual);
    }
    try ( final JsonReader jsonReader = new JsonReader(new InputStreamReader(openJsonInputStream())) ) {
        final JsonElement jsonTree = JsonParser.parseReader(jsonReader);
        final Iterable<String> actual = jsonTree.getAsJsonObject()
                .entrySet()
                .stream()
                .flatMap(letterEntry -> letterEntry.getValue().getAsJsonObject().entrySet().stream()
                        .flatMap(nameEntry -> StreamSupport.stream(nameEntry.getValue().getAsJsonArray().spliterator(), false))
                        .map(preferenceElement -> preferenceElement.getAsJsonObject()
                                .get("path")
                                .getAsString()
                        )
                )
                .collect(ImmutableList.toImmutableList());
        Assertions.assertIterableEquals(expected, actual);
    }
}

如果要反序列化JSON文档,您可以告诉Gson JSON文档的具体类型。请注意,由于缺少正确的参数化,原始Map和List可能无法正常工作。在编译时,最简单和类型安全的方法是描述一个类型标记,并让javac验证类型参数是否安全并与泛型类型签名匹配。当然,您也可以使用其他TypeParameterizedType实现,但这似乎几乎是完美的(除了每个类型标记定义都会创建一个新类(那些...$1.class等))。

代码语言:javascript
复制
private static final TypeToken<Map<String, Map<String, List<Map<String, String>>>>> elementMapTypeToken
        = new TypeToken<Map<String, Map<String, List<Map<String, String>>>>>() {};

private static final Gson gson = new GsonBuilder()
        .disableHtmlEscaping()
        .disableInnerClassSerialization()
        .create();

@Test
public void testWithDynamicMapping()
        throws IOException {
    try ( final JsonReader jsonReader = new JsonReader(new InputStreamReader(openJsonInputStream())) ) {
        final Map<String, Map<String, List<Map<String, String>>>> map = gson.fromJson(jsonReader, elementMapTypeToken.getType());
        final Iterable<String> actual = map.entrySet()
                .stream()
                .flatMap(letterEntry -> letterEntry.getValue().values().stream())
                .flatMap(Collection::stream)
                .map(preference -> preference.get("path"))
                .collect(ImmutableList.toImmutableList());
        Assertions.assertIterableEquals(expected, actual);
    }
}

如果将整个JSON文档加载到内存中可能会耗费大量内存和资源/时间,那么您可以使用流,只需将所需内容逐个移植到JSON令牌中即可。当然,它可能看起来太难实现了,但有时没有其他方法(顺便说一句,这种方法也可以处理无限流)。这是最快的方法,也可以与反序列化( Gson.fromJson(...)系列方法)结合使用,以选择更复杂的值,而不仅仅是简单的值。

代码语言:javascript
复制
@Test
public void testWithStreaming()
        throws IOException {
    try ( final JsonReader jsonReader = new JsonReader(new InputStreamReader(openJsonInputStream())) ) {
        jsonReader.beginObject();
        final Collection<String> actual = new ArrayList<>();
        while ( jsonReader.hasNext() ) {
            jsonReader.nextName();
            jsonReader.beginObject();
            while ( jsonReader.hasNext() ) {
                jsonReader.nextName();
                jsonReader.beginArray();
                while ( jsonReader.hasNext() ) {
                    jsonReader.beginObject();
                    while ( jsonReader.hasNext() ) {
                        final String name = jsonReader.nextName();
                        switch ( name ) {
                        case "path":
                            actual.add(jsonReader.nextString());
                            break;
                        default:
                            jsonReader.skipValue();
                            break;
                        }
                    }
                    jsonReader.endObject();
                }
                jsonReader.endArray();
            }
            jsonReader.endObject();
        }
        jsonReader.endObject();
        Assertions.assertIterableEquals(expected, actual);
    }
}

最后,您还可以使用查询库,比如JsonPath,它可以转换描述JSON path的简单DSL并遍历JSON文档本身。我不确定这种方法有多耗费内存和时间,但我猜它远不是最理想的,但由于DSL,它非常简单。

代码语言:javascript
复制
private static final Configuration jsonPathConfiguration = Configuration.builder()
        .jsonProvider(new GsonJsonProvider(gson))
        .mappingProvider(new GsonMappingProvider(gson))
        .build();

@Test
public void testWithJsonPath()
        throws IOException {
    final ParseContext parseContext = JsonPath.using(jsonPathConfiguration);
    try ( final InputStream inputStream = openJsonInputStream() ) {
        final DocumentContext documentContext = parseContext.parse(inputStream);
        final Iterable<String> actual = StreamSupport.stream(documentContext.read("$.*.*.*.path", JsonArray.class).spliterator(), false)
                .map(JsonElement::getAsString)
                .collect(ImmutableList.toImmutableList());
        Assertions.assertIterableEquals(expected, actual);
    }
}

我还将JSON放入一个资源中,并使用自定义openJsonInputStream()读取它,以避免繁重的JSON出现在代码中。

代码语言:javascript
复制
private static InputStream openJsonInputStream() {
    return PreferenceTest.class.getResourceAsStream("preferences.json");
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67105342

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档