java.lang.ClassCastException:com.google.gson.internal.Linked



我正在尝试将json解析为java对象。在这样做的同时,我得到了一个例外";java.lang.ClassCastException:com.google.gson.internal.LinkedTreeMap不能强制转换为com.google.gson.internal.LinkedTreeMap"。不知怎么的,有时这个代码工作得很好。这是代码。

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。请帮助

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

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

是和否。Gson更像是一种JSON解析/写入和序列化/反序列化。如果问题接近Gson中通常是如何完成的,那么您描述了什么,而Gson不是JSON查询库。

我浏览了一些博客,但没有找到任何其他解决方案,也无法直接将其适应任何pojo。

除非你知道所有的键,并确保你可以在DTO中描述它们,否则你必须使用映射。

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

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,因此它可能是使用GsonJsonElement及其子类来表示任何JSON元素的最快、最健壮和最原生的方式。请注意,此处不显示反序列化和Map

@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文档,您可以告诉GsonJSON文档的具体类型。请注意,由于缺乏正确的参数化,原始Map和List可能无法正常工作。编译时最简单和类型安全的方法是描述类型标记,并使javac验证类型参数是否安全并匹配泛型类型签名。当然,您可以使用其他TypeParameterizedType实现,但这似乎几乎是完美的(除了每个类型令牌定义都会创建一个新类(这些...$1.class等等((。

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(...)方法家族(相结合,以选择更复杂的值,而不仅仅是简单的值。

@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路径的简单DSL,并遍历JSON文档。我不确定这种方法有多消耗内存和时间,但我想它远不是最理想的方法,但由于DSL的原因,它非常简单。

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。

private static InputStream openJsonInputStream() {
return PreferenceTest.class.getResourceAsStream("preferences.json");
}

最新更新