使用 GSON 在字符串和字节 [] 之间转换 JSON

Posted

技术标签:

【中文标题】使用 GSON 在字符串和字节 [] 之间转换 JSON【英文标题】:Converting JSON between string and byte[] with GSON 【发布时间】: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 【参考方案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 值。有什么想法吗?

以上是关于使用 GSON 在字符串和字节 [] 之间转换 JSON的主要内容,如果未能解决你的问题,请参考以下文章

使用Google的Gson实现对象和json字符串之间的转换

Gson 源码解读

Java Gson 实现 Json 数据的生成与解析

Gson(http://www.jianshu.com/p/e740196225a4)

Gson的使用

J07-Java IO流总结七 《 InputStreamReader和OutputStreamWriter 》