【问题标题】:Converting JSON between string and byte[] with GSON使用 GSON 在字符串和字节 [] 之间转换 JSON
【发布时间】:2014-10-20 18:03:04
【问题描述】:

我正在使用休眠将对象映射到数据库。客户端(iOS 应用程序)以 JSON 格式向我发送特定对象,我使用以下实用方法将其转换为它们的真实表示:

/**
     * Convert any json string to a relevant object type
     * @param jsonString the string to convert
     * @param classType the class to convert it too
     * @return the Object created
     */
    public static <T> T getObjectFromJSONString(String jsonString, Class<T> classType) {
        
        if(stringEmptyOrNull(jsonString) || classType == null){
            throw new IllegalArgumentException("Cannot convert null or empty json to object");
        }

        try(Reader reader = new StringReader(jsonString)){
            Gson gson = new GsonBuilder().create();
            return gson.fromJson(reader, classType);
        } catch (IOException e) {
            Logger.error("Unable to close the reader when getting object as string", e);
        }
        return null;
    }

但问题是,在我的 pogo 中,我将值存储为 byte[],如下所示(因为这是存储在数据库中的内容 - 一个 blob):

@Entity
@Table(name = "PersonalCard")
public class PersonalCard implements Card{
    
    @Id @GeneratedValue
    @Column(name = "id")
    private int id;
    
    @OneToOne
    @JoinColumn(name="userid")
    private int userid;
    
    @Column(name = "homephonenumber")
    protected String homeContactNumber;
    
    @Column(name = "mobilephonenumber")
    protected String mobileContactNumber;
    
    @Column(name = "photo")
    private byte[] optionalImage;
    
    @Column(name = "address")
    private String address;

当然,转换失败了,因为它无法在 byte[] 和 String 之间进行转换。

这里最好的方法是改变构造函数以接受字符串而不是字节数组,然后在设置字节数组值的同时自己进行转换,还是有更好的方法来做到这一点。

抛出的错误如下;

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: 预期为 BEGIN_ARRAY,但在第 1 行第 96 列路径为 STRING $.optionalImage

谢谢。

编辑事实上,由于 GSON 生成对象的方式,即使我建议的方法也行不通。

【问题讨论】:

  • 有什么问题?您不能使用 json 格式序列化/反序列化字节数组吗?还是什么?
  • 我正在接收一个 json 对象并将其直接转换为我的休眠 pojo。 hibernate pojo 以字节数组格式工作。 GSON 库将字符串转换为对象,似乎无法转换字节 []
  • 失败的错误是什么?
  • 已添加到 OP
  • 也许这可以帮助你link

标签: java arrays json hibernate gson


【解决方案1】:

这对我有用:

Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss")
        .registerTypeAdapter(byte[].class, (JsonSerializer<byte[]>) (src, typeOfSrc, context) -> new JsonPrimitive(new String(src)))
        .registerTypeAdapter(byte[].class, (JsonDeserializer<byte[]>) (json, typeOfT, context) -> json == null ? null : json.getAsString() == null ? null : json.getAsString().getBytes())
        .create();

【讨论】:

    【解决方案2】:

    我需要符合OpenAPI specification 的解决方案,该OpenAPI specification 声明格式字节的字符串类型必须保存base64 编码值。 POJO 持有 byte[] 且 JSON 对象有 String 属性。

    在我的情况下,我无法使用 android 库进行 base64 处理,因为后端应用程序中使用了 Gson 序列化。出于这个原因,我使用了 java 提供的 java.util.Base64 类。

    我使用了 TypeAdapter 适配器,而不是基于 JsonSerializer,JsonDeserializer 接口的适配器。有了它,我可以覆盖 Gson 的 HTML 转义功能,以防止在 base64 编码中转义填充字符“=”。如果不这样做,那么 "hello World" 的编码值将是 "aGVsbG8gV29ybGQ\u003d" 而不是 "aGVsbG8gV29ybGQ=" 并且反序列化器会失败。

    下面是自定义适配器的代码:

    public class ByteArrayAdapter extends TypeAdapter<byte[]> {
    
      @Override
      public void write(JsonWriter out, byte[] byteValue) throws IOException {
        boolean oldHtmlSafe = out.isHtmlSafe();
        try {
          out.setHtmlSafe(false);
          out.value(new String(Base64.getEncoder().encode(byteValue)));
        } finally {
          out.setHtmlSafe(oldHtmlSafe);
        }
      }
    
      @Override
      public byte[] read(JsonReader in) {
        try {
          if (in.peek() == JsonToken.NULL) {
            in.nextNull();
            return new byte[]{};
          }
          String byteValue = in.nextString();
          if (byteValue != null) {
            return Base64.getDecoder().decode(byteValue);
          }
          return new byte[]{};
        } catch (Exception e) {
          throw new JsonParseException(e);
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      你可以在 POJO 中简单地将照片作为 String ,在 Setter 方法中将 String 转换为 byte[] 并在 Getter 方法中返回 byte[]

      @Entity
      @Table(name = "PersonalCard")
      public class PersonalCard implements Card
      {
      
          @Id @GeneratedValue
          @Column(name = "id")
          private int id;
      
          @OneToOne
          @JoinColumn(name="userid")
          private int userid;
      
          @Column(name = "homephonenumber")
          protected String homeContactNumber;
      
          @Column(name = "mobilephonenumber")
          protected String mobileContactNumber;
      
          @Column(name = "photo")
          private byte[] optionalImage;
      
          @Column(name = "address")
          private String address;
      
          @Column
          byte[] optionalImage;
      
          public byte[] getOptionalImage()
          {
              return optionalImage;
          }
      
          public void setOptionalImage(String s)
          {
              this.optionalImage= s.getBytes();
          }
      }
      

      【讨论】:

      • 我们不应该这样做。
      【解决方案4】:

      来自一些博客以备将来参考,如果链接不可用,至少用户可以参考这里。

      import com.google.gson.Gson;
      import com.google.gson.GsonBuilder;
      import com.google.gson.JsonDeserializationContext;
      import com.google.gson.JsonDeserializer;
      import com.google.gson.JsonElement;
      import com.google.gson.JsonParseException;
      import com.google.gson.JsonPrimitive;
      import com.google.gson.JsonSerializationContext;
      import com.google.gson.JsonSerializer;
      
      import java.lang.reflect.Type;
      import java.util.Date;
      
      public class GsonHelper {
          public static final Gson customGson = new GsonBuilder()
                  .registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
                      @Override
                      public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
                      return new Date(json.getAsLong());
                      }
                  })
                  .registerTypeHierarchyAdapter(byte[].class,
                          new ByteArrayToBase64TypeAdapter()).create();
      
          // Using Android's base64 libraries. This can be replaced with any base64 library.
          private static class ByteArrayToBase64TypeAdapter implements JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
              public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
                  return Base64.decode(json.getAsString(), Base64.NO_WRAP);
              }
      
              public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
                  return new JsonPrimitive(Base64.encodeToString(src, Base64.NO_WRAP));
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        您可以使用此adapter 来序列化和反序列化 base64 中的字节数组。 这是内容。

           public static final Gson customGson = new GsonBuilder().registerTypeHierarchyAdapter(byte[].class,
                    new ByteArrayToBase64TypeAdapter()).create();
        
            // Using Android's base64 libraries. This can be replaced with any base64 library.
            private static class ByteArrayToBase64TypeAdapter implements JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
                public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
                    return Base64.decode(json.getAsString(), Base64.NO_WRAP);
                }
        
                public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
                    return new JsonPrimitive(Base64.encodeToString(src, Base64.NO_WRAP));
                }
            }
        

        感谢作者Ori Peleg

        【讨论】:

        • 我有一个 HashMap - 它似乎不适用于适配器 toJson - 我得到 Java 对象引用而不是目标 base64 值。有什么想法吗?
        猜你喜欢
        • 1970-01-01
        • 2013-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-24
        • 2015-09-16
        • 1970-01-01
        相关资源
        最近更新 更多