如果发生错误,数据栏将是这样的字符串:
{
"code": 2001,
"data": "Error!"
}
如果成功,数据字段将是一个对象:
{
"code": 2000,
"data": {
"id": 1,
"name": "example"
}
}
我使用以下Kotlin代码对其进行反序列化:
return Gson().fromJson(data, SimpleModel::class.java)
模型定义如下:
import com.google.gson.annotations.SerializedName
class SimpleModel {
@SerializedName("code")
val code = 0
@SerializedName("data")
val data: SimpleData = SimpleData()
}
class SimpleData {
@SerializedName("id")
val id = ""
@SerializedName("name")
val name = ""
}
当没有错误发生时,上面的代码工作正常。但是当错误发生时,会引发异常:
java.lang.IllegalStateException: Excepted BEGIN_OBJECT but was STRING at line 1 column x $path.data
有没有办法将数据
字段反序列化为对象或任何对象,并通过代码手动确定其类型?
像前面的答案一样,我不熟悉静态编程语言,下面的解决方案Java,但正如我所知,使用IntelliJ内置工具很容易将Java转换为静态编程语言。
成功/错误对象对是一个经典问题,您可以创建自己的解决方法,但是我们考虑以下类分别表示成功和错误对象(java 17,然后启用交换机上的模式匹配):
abstract sealed class SimpleModel<T>
permits SimpleModelSuccess, SimpleModelError {
@SerializedName("code")
final int code;
SimpleModel(final int code) {
this.code = code;
}
}
final class SimpleModelSuccess<T>
extends SimpleModel<T> {
@SerializedName("data")
final T data;
private SimpleModelSuccess(final int code, final T data) {
super(code);
this.data = data;
}
}
final class SimpleModelError<T>
extends SimpleModel<T> {
@SerializedName("data") // the annotation is helping here!
final String message;
private SimpleModelError(final int code, final String message) {
super(code);
this.message = message;
}
}
上面的代码可以自我解释。现在的核心部分需要比我之前想象的更多的工作,我给你们提供了我的第一条评论,这似乎是不完整的。
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
final class SimpleModelTypeAdapterFactory
implements TypeAdapterFactory {
@Getter
private static final TypeAdapterFactory instance = new SimpleModelTypeAdapterFactory();
@Override
@Nullable
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( !SimpleModel.class.isAssignableFrom(typeToken.getRawType()) ) {
return null;
}
// let's figure out what the model is parameterized with
final Type type = typeToken.getType();
final Type typeParameter;
if ( type instanceof ParameterizedType parameterizedType ) {
typeParameter = parameterizedType.getActualTypeArguments()[0];
} else {
throw new UnsupportedOperationException("Cannot infer type parameter from " + type);
}
// then borrow their respective type adapters for both success and error cases
@SuppressWarnings("unchecked")
final TypeAdapter<T> successDelegate = (TypeAdapter<T>) gson.getDelegateAdapter(this, TypeToken.getParameterized(SimpleModelSuccess.class, typeParameter));
@SuppressWarnings("unchecked")
final TypeAdapter<T> errorDelegate = (TypeAdapter<T>) gson.getDelegateAdapter(this, TypeToken.getParameterized(SimpleModelError.class, typeParameter));
return new TypeAdapter<>() {
@Override
public void write(final JsonWriter out, final T value) {
throw new UnsupportedOperationException();
}
@Override
public T read(final JsonReader in) {
// buffer the JSON tree first
// note that this solution may be very inefficient under some circumstances
final JsonObject buffer = Streams.parse(in).getAsJsonObject();
final JsonElement dataElement = buffer.get("data");
// is it's data is {...}, the consider it is success (by the way, what is code about?)
if ( dataElement.isJsonObject() ) {
return successDelegate.fromJsonTree(buffer);
}
// if it's a primitive, consider it's an error
if ( dataElement.isJsonPrimitive() ) {
return errorDelegate.fromJsonTree(buffer);
}
// well we've done our best...
throw new JsonParseException(String.format("Cannot deduce the model for %s", buffer.getClass()));
}
};
}
}
java prettyprint-override">public final class SimpleModelTypeAdapterFactoryTest {
private static final class SomeJsonProvider
implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(final ExtensionContext context) {
return Stream.of(
Arguments.of(
"""
{
"code": 2000,
"data": {
"id": 1,
"name": "example"
}
}
"""
),
Arguments.of(
"""
{
"code": 2001,
"data": "Error!"
}
"""
)
);
}
}
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@ToString
private static final class SimpleData {
private final String id;
private final String name;
}
private static final Type simpleModelOfSimpleDataType = TypeToken.getParameterized(SimpleModel.class, SimpleData.class)
.getType();
@ParameterizedTest
@ArgumentsSource(SomeJsonProvider.class)
public void test(final String json) {
final Gson gson = new GsonBuilder()
.disableHtmlEscaping()
.disableInnerClassSerialization()
.registerTypeAdapterFactory(SimpleModelTypeAdapterFactory.getInstance())
.create();
final SimpleModel<SimpleData> model = gson.fromJson(json, simpleModelOfSimpleDataType);
switch ( model ) {
case SimpleModelSuccess<SimpleData> success -> System.out.println(success.data);
case SimpleModelError<SimpleData> error -> System.out.println(error.message);
}
}
}
以下是它打印到标准输出的内容:
SimpleModelTypeAdapterFactoryTest. SimpleData(id=1, name=示例)
错误!
嗯,是的,这比我第一次评论中提到的要“有点”棘手。
您需要编写一个自定义的反序列化程序,它根据节点的运行时类型决定将数据反序列化到哪个类型,并将该反序列化程序注册到
Gson
实例中。不幸的是,我不熟悉kotlin语法,所以我只能给出伪代码。
SimpleModel
中的字段data
应该是Object
,或者将类设置为泛型-SimpleModel
JsonElement root = parseResponse();
root.getAsJsonObject().get("data").getAsString();
你可以用我的答案作为灵感。虽然它是关于对象映射器的,但它做同样的事情——根据节点类型决定对象类型,并遵循与我上面描述的大致相同的算法。
此外,本指南还提供了有关如何编写或注册自己的反序列化程序的信息。
是否有方法序列化类的瞬态字段?文档中提到默认情况下不支持它,但是否有办法打开它? 非常感谢
问题内容: 我有以下需要反序列化的Json字符串。 第一个字段“ 123456789”是一个ID号,因此基本上该值可以根据要查询的数据而有所不同。 我在Visual Studio中使用C#。显然,因为第一个字段的值可以更改,所以我无法使用预定义的类将JSON反序列化为该字段,因为该字段将用作类名,但该字段的值与该类名不匹配。 有没有一种方法可以将其反序列化为某种动态类,但仍可以像访问预定义类一样访
问题内容: 在Java中进行序列化后,是否可以将字段设置为任何非默认值?我的用例是一个缓存变量- 这就是为什么。我也有一个习惯,即不要更改字段(即,地图的内容已更改,但对象本身保持不变)。但是,这些属性似乎是矛盾的- 尽管编译器允许这样的组合,但除反序列化之后,我无法将字段设置为任何值。 我尝试了以下操作,但没有成功: 简单的字段初始化(如示例所示):这是我通常所做的,但是在未序列化之后似乎没有发
考虑以下代码: 现在扩展了一个实现接口的类。类和是带有一堆getter和setter的POJOS。FindBugs抱怨和字段说: 这个Serializable类定义了一个非基元实例字段,它既不是瞬态的、Serializable的,也不java.lang.Object的,并且似乎没有实现Externalizable接口或readObject()和WriteObject()方法。 好吧,所以一切都很好
从我的Java后端,我正在使用另一个我不管理的后端,它的API定义不可用。我正在创建它的服务的OpenAPI定义,并使用Swagger Codigen来生成客户端。 有一个endpoint返回一个复杂对象: 在该对象中,和始终存在,但在同一级别上有数百个动态项。在上面的示例中,关键点是可以预测的,但实际上是字母和数字的序列,类似于“245df921”。动态项始终是具有相同项数和相同预期位置的数组。
我有一个没有扩展可串行化或可外部化接口的类,但在netbeans中使用FindBugs时仍然会遇到错误。有人能告诉我如何解决这个问题吗? 这是我的课 FindBug中的Bug:类分析器定义了一个非瞬时的非序列化实例字段objAnalyzerVar,对于objAnalyzerDataTypeInfo也是如此;