- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
是否有可能实现以下两个目标?
下面我描述了两种可能的方法,但只能实现其中一种。
<小时/>我正在使用的 API 返回成功的结果,例如:
{
"type": "success",
"data": {
"display_name": "Invisible Pink Unicorn",
"user_email": "user@example.com",
"user_id": 1234
}
}
或者一个错误,例如:
{
"type": "error",
"data": {
"error_name": "incorrect_password",
"error_message": "The username or password you entered is incorrect."
}
}
目前的处理方式是注册一个 TypeAdapter,如果类型为 “error”
,则该 TypeAdapter 会抛出带有给定 “error_message”
的异常:
new GsonBuilder()
.registerTypeAdapter(User.class, new ContentDeserializer<User>())
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
public class ContentDeserializer<T> implements JsonDeserializer<T> {
@Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
final JsonObject object = json.getAsJsonObject();
final String type = object.get("type").getAsString();
final JsonElement data = object.get("data");
final Gson gson = new Gson();
if ("error".equals(type)) {
throw gson.fromJson(data, ApiError.class);
} else {
return gson.fromJson(data, typeOfT);
}
}
}
这很简洁,因为它非常简洁,并且使用默认的反序列化器来完成所有艰苦的工作。
但实际上这是错误的,因为它不使用相同的 Gson 来委派该工作,因此它会使用不同的字段命名策略,例如。
为了解决这个问题,我编写了一个 TypeAdapterFactory:
public class UserAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (!User.class.isAssignableFrom(type.getRawType())) return null;
final TypeAdapter<User> userAdapter = (TypeAdapter<User>) gson.getDelegateAdapter(this, type);
final TypeAdapter<ApiError> apiErrorAdapter = gson.getAdapter(ApiError.class);
return (TypeAdapter<T>) new Adapter(userAdapter, apiErrorAdapter);
}
private static class Adapter extends TypeAdapter<User> {
private final TypeAdapter<User> userAdapter;
private final TypeAdapter<ApiError> apiErrorAdapter;
Adapter(TypeAdapter<User> userAdapter, TypeAdapter<ApiError> apiErrorAdapter) {
this.userAdapter = userAdapter;
this.apiErrorAdapter = apiErrorAdapter;
}
@Override
public void write(JsonWriter out, User value) throws IOException {
}
@Override
public User read(JsonReader in) throws IOException {
User user = null;
String type = null;
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "type":
type = in.nextString();
break;
case "data":
if ("error".equals(type)) {
throw apiErrorAdapter.read(in);
} else if ("success".equals(type)) {
user = userAdapter.read(in);
}
break;
}
}
in.endObject();
return user;
}
}
}
这需要做更多的工作,但至少让我可以委托(delegate)给相同的 Gson 配置。
这种方法的问题是,当 JSON 对象具有不同的顺序时,它就会中断:
{
"data": {
"display_name": "Invisible Pink Unicorn",
"user_email": "user@example.com",
"user_id": 1234
},
"type": "success"
}
我没有看到任何解决办法,因为我认为 JsonReader
没有读取输入两次的选项,也没有办法将“数据”值缓存在抽象类型,如 JsonElement
,在遇到“type”后进行解析。
最佳答案
But actually it's wrong, because it doesn't use the same Gson to delegate that work to, so it will use a different field naming policy, for example.
正确。您应该使用JsonDeserializationContext
.
... because I don't think JsonReader has an option to read the input twice, there's also no way to cache the "data" value in an abstract type like JsonElement to parse after "type" has been encountered.
正确。 JsonReader
是一个流阅读器,而 JsonElement
是一棵树。它们就像 XML 世界中的 SAX 和 DOM,各有优缺点。流式读取器只是读取输入流,您必须自己缓冲/缓存中间数据。
对于您的情况,您可以使用这两种方法,但我会选择 JsonDeserializer
因为它的简单性(假设您不打算编写超快的解串器)。
我不太确定你的User
怎么样和ApiError
彼此相关,但我会为两种不同类型的值使用一个公共(public)类:真实值和错误。看起来你的两个类有一个共同的父类或祖先,但我不太确定你如何在调用站点处理它们(也许instanceof
?)。比如说,像这样的东西(隐藏构造函数以封装对象结构初始化的复杂性):
final class Content<T> {
private final boolean isSuccess;
private final T data;
private final ApiError error;
private Content(final boolean isSuccess, final T data, final ApiError error) {
this.isSuccess = isSuccess;
this.data = data;
this.error = error;
}
static <T> Content<T> success(final T data) {
return new Content<>(true, data, null);
}
static <T> Content<T> error(final ApiError error) {
return new Content<>(false, null, error);
}
boolean isSuccess() {
return isSuccess;
}
T getData()
throws IllegalStateException {
if ( !isSuccess ) {
throw new IllegalStateException();
}
return data;
}
ApiError getError()
throws IllegalStateException {
if ( isSuccess ) {
throw new IllegalStateException();
}
return error;
}
}
两者都是User
和ApiError
从我的角度来看(我更喜欢@SerializedName
,尽管这样可以更好地控制命名——但这似乎是一个习惯问题)。
final class ApiError {
@SuppressWarnings("error_name")
final String errorName = null;
@SerializedName("error_message")
final String errorMessage = null;
}
final class User {
@SerializedName("display_name")
final String displayName = null;
@SerializedName("user_email")
final String userEmail = null;
@SuppressWarnings("user_id")
final int userId = Integer.valueOf(0);
}
接下来,由于树操作更容易,只需实现 JSON 反序列化器即可:
final class ContentJsonDeserializer<T>
implements JsonDeserializer<Content<T>> {
// This deserializer holds no state
private static final JsonDeserializer<?> contentJsonDeserializer = new ContentJsonDeserializer<>();
private ContentJsonDeserializer() {
}
// ... and we hide away that fact not letting this one to be instantiated at call sites
static <T> JsonDeserializer<T> getContentJsonDeserializer() {
// Narrowing down the @SuppressWarnings scope -- suppressing warnings for entire method may be harmful
@SuppressWarnings("unchecked")
final JsonDeserializer<T> contentJsonDeserializer = (JsonDeserializer<T>) ContentJsonDeserializer.contentJsonDeserializer;
return contentJsonDeserializer;
}
@Override
public Content<T> deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
throws JsonParseException {
final JsonObject jsonObject = jsonElement.getAsJsonObject();
final String responseType = jsonObject.getAsJsonPrimitive("type").getAsString();
switch ( responseType ) {
case "success":
return success(context.deserialize(jsonObject.get("data"), getTypeParameter0(type)));
case "error":
return error(context.deserialize(jsonObject.get("data"), ApiError.class));
default:
throw new JsonParseException(responseType);
}
}
// Trying to detect any given type parameterization for its first type parameter
private static Type getTypeParameter0(final Type type) {
if ( !(type instanceof ParameterizedType) ) {
return Object.class;
}
return ((ParameterizedType) type).getActualTypeArguments()[0];
}
}
演示:
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(Content.class, getContentJsonDeserializer())
.create();
private static final Type userContent = new TypeToken<Content<User>>() {
}.getType();
public static void main(final String... args)
throws IOException {
for ( final String name : ImmutableList.of("success.json", "error.json", "success-reversed.json", "error-reversed.json") ) {
try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q44400163.class, name) ) {
final Content<User> content = gson.fromJson(jsonReader, userContent);
if ( content.isSuccess() ) {
System.out.println("SUCCESS: " + content.getData().displayName);
} else {
System.out.println("ERROR: " + content.getError().errorMessage);
}
}
}
}
输出:
SUCCESS: Invisible Pink Unicorn
ERROR: The username or password you entered is incorrect.
SUCCESS: Invisible Pink Unicorn
ERROR: The username or password you entered is incorrect.
现在,回到关于 TypeAdapter
的原始问题。正如我上面提到的,您也可以使用类型适配器来完成此操作,但您必须实现两种情况支持:
type
首先属性,然后阅读data
根据您的实际日期类型的属性。顺便说一下,你的TypeAdapter
实现远非通用:您必须使用 Gson.getDelegateAdapter
解析真实的数据类型及其适配器。 .data
属性到 TreeView 中(因此将其缓冲到内存中)作为 JsonElement
实例(必须先从TypeAdapter<JsonElement>
方法中的Gson
实例获取create
),然后根据下一个type
属性值,使用 TypeAdapter.fromJsonTree
将其作为值从树中读取.是的,不要忘记检查此处的解析状态(以某种方式处理两种情况下缺失的 type
和 data
)。正如您所看到的,这引入了可变的复杂性和性能/内存成本,但它可以为您提供最佳性能。你决定。
关于java - 使用 Gson TypeAdapter 以顺序不敏感的方式反序列化 JSON 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44400163/
我正在创建一个有效的突变,但我不确定它是否按照我认为的方式工作。但是,我想知道执行顺序是什么? 异步 从上到下同步 同步随机顺序 其他 我想确保在执行插入/更新插入之前从表中删除某些项目。使用以下突变
如何更改规则中的前提顺序? 例如,在伊莎贝尔的自然演绎规则中: mp: ?P ⟶ ?Q ⟹ ?P ⟹ ?Q 我们可以将顺序更改为: ?P ⟹ ?P ⟶ ?Q ⟹ ?Q 我可以用 rev_mp或者定义一
关闭。这个问题需要details or clarity .它目前不接受答案。 想改善这个问题吗?通过 editing this post 添加详细信息并澄清问题. 8年前关闭。 Improve thi
我正在使用 Hibernate 3.2,并使用标准来构建查询。我想为多对一关联添加和“排序”,但我不知道如何做到这一点。 Hibernate 查询最终看起来像这样,我猜: select t1.a, t
我正在开发一个项目,但无法让我的 javascript 按顺序工作。我知道 javascript 可以并行执行任务,因此当您向不响应的服务器发出请求时,它不会被卡住。这有它的优点和缺点。就我而言,这是
在下面的代码中,我认为f1 > f2 > f3是调用顺序,但是仅f1被调用。如何获得依次调用的3个函数? 我已经将以下内容添加到main函数中,它可以按预期工作,但是我想知道是否还有其他确定的方法可以
我有一个如下所示的对象数组: [{ "id": 1, "Size": 90, "Maturity": 24, }, { "id": 2, "S
这是征求意见和要求的请求。我是Docker的新手。 我想要一个用于Python项目的生产和开发容器(可能也进行单元测试)。我的搜索指向多阶段Dockerfile(以及运行它们的多个docker-com
我想知道解决以下问题的有效方法是什么: 假设我在组 1 中有三个字符,在组 2 中有两个字符: group_1 = c("X", "Y", "Z") group_2 = c("A", "B") 显然,
在 Cordova 网站上,您可以看到一长串按字母顺序排列的钩子(Hook)列表,但它们触发和执行的正确顺序是什么? 我正在尝试在构建/编译之前将 cordova.js 脚本添加到 index.htm
我想知道解决以下问题的有效方法是什么: 假设我在组 1 中有三个字符,在组 2 中有两个字符: group_1 = c("X", "Y", "Z") group_2 = c("A", "B") 显然,
这个问题已经有答案了: 奥 git _a (2 个回答) 已关闭 9 年前。 这是我的一个练习的代码, public class RockTest { public static void main(
我使用 HashMap 来存储一些数据,但每当新数据保存到 HashMap 或旧数据移出 HashMap 时,我都需要将其保持升序。但是hashmap本身不支持顺序,我可以使用什么数据结构来支持顺序?
我想创建一个序列,当星期几与函数参数中的日期相同时,它会返回所有年份的结果(例如:自开始日期起,2 月 12 日为星期日的所有年份)。 let myDate (dw:System.DayOfWeek)
我有一个包含许多元素的 Xelement。 我有以下代码来对它们进行排序: var calculation = from y in x.Elements("row")
假设我有: 在 javacript 文件中,我为类按钮和 ID 名称定义了点击操作,例如: $("#name").click(function(event){ alert("hi"); }) $
我有一个包含 2 个 subview 的 View - collectionView 和自定义 View 。我想设置一个操作在布置 2 个 View 后运行,但layoutSubViews 运行了两次
关闭。这个问题需要更多 focused .它目前不接受答案。 想改进这个问题?更新问题,使其仅关注一个问题 editing this post . 2年前关闭。 Improve this questi
我想知道 C++ 中是否有内置方法来比较两个双向迭代器的顺序。例如,我有一个 Sum 函数来计算同一列表中 2 个迭代器之间的总和: double Sum(std::list::const_itera
在 MySQL 中,这两个查询之间有区别吗? SELECT * FROM .... ORDER BY Created,Id DESC 和 SELECT * FROM .... ORDER BY Cre
我是一名优秀的程序员,十分优秀!