GSON 整数转换为特定字段的布尔值

15

我正在处理一个API,它返回整数(1=true,其他=false)来表示布尔值。

我看到了这个问题和答案,但我需要能够指定应该应用于哪个字段,因为有时整数实际上是整数。

编辑:传入的JSON可能如下所示(也可能是字符串而不是整数等):

{ 
    "regular_int": 1234, 
    "int_that_should_be_a_boolean": 1
}

我需要一种方法来指定int_that_should_be_a_boolean应该被解析为布尔值,而regular_int应该被解析为整数。

2个回答

35

我们将为Gson提供一个小钩子,即一个自定义的布尔值反序列化器,也就是实现了JsonDeserializer<Boolean>接口的类:

CustomBooleanTypeAdapter

import java.lang.reflect.Type;
import com.google.gson.*;
class BooleanTypeAdapter implements JsonDeserializer<Boolean> {
public Boolean deserialize(JsonElement json, Type typeOfT,
                           JsonDeserializationContext context) throws JsonParseException {
    if (((JsonPrimitive) json).isBoolean()) {
        return json.getAsBoolean();
    }
    if (((JsonPrimitive) json).isString()) {
        String jsonValue = json.getAsString();
        if (jsonValue.equalsIgnoreCase("true")) {
            return true;
        } else if (jsonValue.equalsIgnoreCase("false")) {
            return false;
        } else {
            return null;
        }
    }

    int code = json.getAsInt();
    return code == 0 ? false :
            code == 1 ? true : null;
  }
}

要使用它,我们需要略微更改获取Gson映射器实例的方式,使用一个工厂对象——GsonBuilder,这是一种常见的模式。下面是使用GSON的示例。

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Boolean.class, new BooleanTypeAdapter());
Gson gson = builder.create();

对于原始类型,请使用下面的内容

 GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapter(boolean.class, new BooleanTypeAdapter());
    Gson gson = builder.create();

享受 JSON 解析吧!


1
谢谢!如果我理解正确,使用这个BooleanTypeAdapter进行反序列化时,任何boolean都会被这样处理? - Eliezer
3
那么关于“特定字段”的问题呢? 这个解决方案对包含普通整数值的响应,比如“quantity: 0”,是不起作用的,因为这个键不能表示为“布尔值”。 - Khalid ElSayed
2
如果我的JSON中有一个普通的布尔字段和由整数值表示的布尔字段,它是如何工作的? - algrid
4
这里的技巧在于Java类中的参数类型决定了要使用哪个适配器。因此,如果quantity是一个int,它将使用标准整数适配器。如果quantity是一个boolean,它将使用自定义布尔适配器(从0或1获取值)。 - Tremelune

1

如果我理解正确,您想将来自JsonReader的int值标准化或转换为其他值,例如布尔值。

如果是这样,您可能需要创建一个扩展TypeAdapter<YourFieldNameType>的适配器类并覆盖read()方法。您可以从nextInt()获取值,然后根据其值返回适当的布尔值。根据解析器的配置,您可能需要检查空值。

如果需要,在同一适配器类中覆盖write()方法,以便将客户端代码中的布尔值强制转换为JsonWriter中的整数。

[编辑]

供参考,这里有一个示例TypeAdapter,它将“Commands”枚举类强制转换为/从整数。

package com.company.product.json;

import static com.company.product.Commands.*;

import java.io.IOException;
import java.util.logging.Logger;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.company.product.Commands;
import com.company.product.client.ClientSocket;

/**
 * Adapter for Command handling.
 * 
 * We write out the CommandName as an Integer, and read it in as a Commands constant.
 * 
 * This satisfies the requirement that the CommandName by represented by JSON as an int, but allows
 * us to deserialize it to a Commands object on read.
 * 
 * @author jdv
 * @see com.company.product.Command#commandName CommandName
 */
public class CommandsAdapter extends TypeAdapter<Commands> {

  private static final Logger logger = Logger.getLogger(ClientSocket.class.getPackage().getName());

  /*
   * (non-Javadoc) Deserialize the JSON "CommandName" integer into the corresponding Commands
   * constant object.
   * 
   * @see com.google.gson.TypeAdapter#read(com.google.gson.stream.JsonReader)
   */
  @Override
  public Commands read(JsonReader in) throws IOException {

    final int command;
    try {
      command = in.nextInt();

    } catch (IllegalStateException e) {
      logger.severe("Unable to read incoming JSON stream: " + e.getMessage());
      throw new IOException(e);

    } catch (NumberFormatException e) {
      logger
          .severe("Unable to read and convert CommandName Integer from the incoming JSON stream: "
              + e.getMessage());
      throw new IOException(e);
    }

    // Let's not risk using a bad array index. Not every command is expected
    // by the WebSocket handlers, but we should do our best to construct
    // a valid Commands object.
    if (command < NO_OP.getValue() || command > LAST_COMMAND.getValue()) {
      throw new IOException(new IllegalArgumentException(
          "Unexpected value encountered for Commands constant: " + command));
    } else {
      return Commands.values()[command];
    }
  }

  /*
   * (non-Javadoc) Serialize Commands object constants as their Integer values.
   * 
   * @see com.google.gson.TypeAdapter#write(com.google.gson.stream.JsonWriter, java.lang.Object)
   */
  @Override
  public void write(JsonWriter out, Commands value) throws IOException {
    out.value(value.getValue());
  }

}

这基本上是将本地表示为“Commands”枚举和远程整数的“CommandName”序列化参数上的传入和传出操作进行调整。与此Commands枚举有关的任何内容都通过此适配器类进行过滤,该类覆盖了read()和write()方法。最终驱动WebSocket对等体,但这对本次讨论并不重要。希望对您有所帮助。

假设有一些整数值,我想将它们视为布尔值,还有一些我想将其视为整数,就像我在问题的最后一句话中所述。 - Eliezer
如果它们来自JsonReader上的同一序列化字段,您将需要在read()中添加额外的逻辑来做出决策并返回正确的内容。但是,简而言之,如果您需要将传入或传出类型混合到其他类型,则需要为这些类型创建TypeAdapter类。 - user1531971
我已经更新了问题,以更清晰地解释问题。 - Eliezer
Gson非常面向类型。据我所知,名称是用于序列化字段的,因此无法在反序列化方面帮助您。您可能需要创建一个新的目标容器类型,将“1”转换为布尔值,或者可能需要创建自己的解析器,以便可以访问字段名称。还有FieldAttributes,这可能是您钩入自定义反序列化器上字段名称的一种方法。 - user1531971

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接