gson是google推出的json解析框架,相较于其他json解析框架,它速度更快也更安全(网上有很多资料,这里就不赘述了),在maven项目中使用gson,只需要引入以下配置即可,这里使用的2.8.5版本
com.google.code.gson gson 2.8.5
- 1
- 2
- 3
- 4
- 5
在springBoot中注入定义的HttpMessageConverter 即可(若使用的其他json框架,注入相应的HttpMessageConverter),例如:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public HttpMessageConverter GsonHttpMessageConverter() {
GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter();
gsonHttpMessageConverter.setGson(new Gson());//放入Gson实例
return gsonHttpMessageConverter;
}
}
在Gson使用过程中,可能遇到多种情况,例如:
第一种方式:
使用JsonDeserializer来指定反序列化(解析)方式,与此对应的还有JsonSerializer接口,用来指定对象序列化方式:
//设定Date类型的反序列化(解析)规则
private static final com.google.gson.JsonDeserializer<Date> deser = new com.google.gson.JsonDeserializer<Date>() {
@Override
public Date deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
String date = json.getAsString();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
format.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
return format.parse(date);
} catch (ParseException exp) {
System.err.println(exp.getMessage());
return null;
}
}
};
private final static Gson INSTANCE = new GsonBuilder().serializeNulls().registerTypeAdapter(Date.class, deser).create();//制定Date类型的转换器
第二种方式:
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private final static Gson INSTANCE = new GsonBuilder().serializeNulls().setDateFormat(DATE_FORMAT).create();//此规则 将用于序列化与反序列化Date对象,如果多次指定此规则,将以最后一次为准
第二种方式=JsonDeserializer+JsonSerializer
如果项目中已经约定好使用统一的日期格式,使用第二种方式更简便易读
在某场景中,原本是double类型的数据,在上百条数据中,有几条数据因为数据为空,我接收到是"-“,一转换就报类型无法转换错误(因为是爬取数据),我只能自己想办法把”-"转为——(double)0
@Test
void contextLoads() {
String dataJson = "{\"number\" : 30.2, \"amount\" : \"-\"}";
Gson gson = GsonUtils.getSingleton();
Map map = gson.fromJson(dataJson, Map.class);
System.out.println(map);
}
解决:
public static final TypeAdapter<Number> NumberFormat = new TypeAdapter<Number>() {
@Override
public Number read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return 0;
}
try {
double i = in.nextDouble();
return i;
} catch (NumberFormatException e) {
//如果报数字格式化异常,判断是否为目标字符串
if (e.getMessage().contains("-")){
Class<JsonReader> jsonReaderClass = JsonReader.class;
try {
Field peekedString = jsonReaderClass.getDeclaredField("peekedString");
peekedString.setAccessible(true);
peekedString.set(in,"0.0");//通过反射更改了json字符串中原"-"为0.0
double i = in.nextDouble();//更改值后重新计算
return i;
} catch (NoSuchFieldException ex) {
ex.printStackTrace();
} catch (IllegalAccessException ex) {
ex.printStackTrace();
}
return 0.0;
}else {
throw new JsonSyntaxException(e);
}
}
}
@Override
public void write(JsonWriter out, Number value) throws IOException {
out.value(value);
}
};
private final static Gson INSTANCE = new GsonBuilder().serializeNulls()
.disableHtmlEscaping()//默认情况下,Gson会转义HTML字符,例如<>等。使用此选项将 Gson配置为直接传递HTML字符
.registerTypeAdapter(Date.class, deser)
.registerTypeAdapter(Double.class, NumberFormat)
.registerTypeAdapter(double.class, NumberFormat)
.create();
由于registerTypeAdapter只注册指定的类型,不会注册相关类型,所以注册Double时,也需要注册double类型,贴出registerTypeAdapter方法源码以及注释:
/**
* Configures Gson for custom serialization or deserialization. This method combines the
* registration of an {@link TypeAdapter}, {@link InstanceCreator}, {@link JsonSerializer}, and a
* {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements
* all the required interfaces for custom serialization with Gson. If a type adapter was
* previously registered for the specified {@code type}, it is overwritten.
*
* This registers the type specified and no other types: you must manually register related
* types! For example, applications registering {@code boolean.class} should also register {@code
* Boolean.class}.
*
* @param type the type definition for the type adapter being registered
* @param typeAdapter This object must implement at least one of the {@link TypeAdapter},
* {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
$Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
|| typeAdapter instanceof JsonDeserializer<?>
|| typeAdapter instanceof InstanceCreator<?>
|| typeAdapter instanceof TypeAdapter<?>);
if (typeAdapter instanceof InstanceCreator<?>) {
instanceCreators.put(type, (InstanceCreator) typeAdapter);
}
if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) {
TypeToken<?> typeToken = TypeToken.get(type);
factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
}
if (typeAdapter instanceof TypeAdapter<?>) {
factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
}
return this;
}
大家不一定会遇到一摸一样的场景,这里只是提供一个思路
在项目中,出现:class declares multiple JSON fields named serialVersionUid异常
解决:
private static final List<String> EXCLUDE = new ArrayList<String>() {{
add("serialVersionUID");
}};
private final static Gson INSTANCE = new GsonBuilder().serializeNulls()
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
boolean exclude = false;
try {
exclude = EXCLUDE.contains(f.getName());
} catch (Exception ignore) {
}
return exclude;
}
@Override
public boolean shouldSkipClass(Class<?> aClass) {
return false;
}
})//制定排除策略
.disableHtmlEscaping()//默认情况下,Gson会转义HTML字符,例如<>等。使用此选项将 Gson配置为直接传递HTML字符
.registerTypeAdapter(Date.class, deser)
.registerTypeAdapter(Double.class, NumberFormat)
.registerTypeAdapter(double.class, NumberFormat)
.create();
参考
Error: Class declares multiple JSON fields named serialVersionUid