我正在尝试将一个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。热心的帮助
发布于 2021-04-15 20:10:19
我不知道为什么会出现异常(不是有勘误表吗?--我想知道为什么在使用相同的类加载器时A类不能转换为A类)。另外,请不要将反序列化(映射到映射或普通对象)和JsonElement子类混为一谈。如果您这样做,那么您必须使用适当的类型,并告诉Gson使用它们。我还强烈建议不要坚持使用具体的类,特别是如果它们位于内部包中--它们可能会在任何完全破坏您的代码的发行版中发生变化。坚持使用接口。
有没有其他更好的方法来解析这个json。
既是也不是。Gson更像是一个JSON解析/编写和序列化/反序列化。如果问题接近于通常在Gson中是如何完成的,那么您所描述的问题只是不是一个JSON查询库。
我浏览了一些博客,但没有找到任何其他解决方案,也不能直接将其改编为任何pojo。
你必须使用地图,除非你知道所有的关键字,并确保你可以用DTO描述它们。
以下是查询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,因此它可能是使用Gson JsonElement及其子类表示任何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文档,您可以告诉Gson JSON文档的具体类型。请注意,由于缺少正确的参数化,原始Map和List可能无法正常工作。在编译时,最简单和类型安全的方法是描述一个类型标记,并让javac验证类型参数是否安全并与泛型类型签名匹配。当然,您也可以使用其他Type或ParameterizedType实现,但这似乎几乎是完美的(除了每个类型标记定义都会创建一个新类(那些...$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 path的简单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");
}https://stackoverflow.com/questions/67105342
复制相似问题