1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GSON:バイト配列とbase64url文字列の双方向変換

Posted at

はじめに

GSON のデフォルト動作では、バイト配列をシリアライズすると、数字の配列となります。

// 次のデータ構造を表す Map インスタンスを作成する
//
//   {
//     "binary": [ 18, 52, 86 ]
//   }
//
Map<String, Object> input = new LinkedHashMap<>();
input.put("binary", { (byte)0x12, (byte)0x34, (byte)0x56 };

// Gson を用意する
Gson gson = new GsonBuilder().setPrettyPrinting().create();

// データを JSON に変換する
String json = gson.toJson(input);

// JSON を出力する
System.out.println(json);
上記コードの出力
{
  "binary": [
    18,
    52,
    86
  ]
}

しかし、場合によっては、バイト配列を base64url 文字列としてシリアライズしたいことがあるかもしれません。

バイト配列をbase64url文字列へとシリアライズした場合
{
  "binary": "EjRW"
}

この記事では、バイト配列と base64url 文字列の双方向変換を GSON に行わせる方法を紹介します。

TypeAdapter

特定の型の変換処理をカスタマイズしたい場合、TypeAdapter のサブクラスを作って GsonBuilder に登録します。

TypeAdapter クラスには、未実装の抽象メソッドが二つあります。一つはシリアラズ処理時に呼ばれる write メソッド、もう一つはデシリアライズ時に呼ばれる read メソッドです。

今回は、write メソッドの実装では「受け取ったバイト配列を base64url 文字列に変換して書き出す」処理を、read メソッドの実装では「base64url 文字列を読み込んでバイト配列に変換して返す」処理を行います。

この処理の実装は、次のようになります。

Base64UrlAdapter.java
Base64UrlAdapter.java
package com.example;


import java.io.IOException;
import java.util.Base64;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;


public class Base64UrlAdapter extends TypeAdapter<byte[]>
{
    @Override
    public void write(JsonWriter out, byte[] value) throws IOException
    {
        if (value == null)
        {
            // null を書き出す
            out.nullValue();
        }
        else
        {
            // バイト配列を base64url 文字列に変換して書き出す
            out.value(toBase64Url(value));
        }
    }


    @Override
    public byte[] read(JsonReader in) throws IOException
    {
        if (in.peek() == JsonToken.NULL)
        {
            // null を読み込み、null を返す
            in.nextNull();
            return null;
        }

        // base64url 文字列を読み込み、バイト配列に変換して返す
        return fromBase64Url(in.nextString());
    }


    private static String toBase64Url(byte[] value)
    {
        // バイト配列を base64url 文字列に変換する
        return Base64.getUrlEncoder().withoutPadding().encodeToString(value);
    }


    private static byte[] fromBase64Url(String value)
    {
        // base64url 文字列をバイト配列に変換する
        return Base64.getUrlDecoder().decode(value);
    }
}

このように実装した Base64UrlAdapterGsonBuilderregisterTypeAdapter メソッドで登録してから Gson インスタンスを作成すれば、準備完了です。

Base64UrAdapter を有効にして Gson を作成する
Gson gson = new GsonBuilder()
        .registerTypeAdapter(byte[].class, new Base64UrlAdapter())
        .create();

コード例

バイト配列から base64url 文字列へのシリアライズ処理と、base64url 文字列からバイト配列へのデシリアライズ処理、それぞれのコード例は次のとおりです。

バイト配列を base64url 文字列へシリアライズする
@Test
public void test_to_base64url()
{
    // Base64UrlAdapter を有効にして Gson を作成する
    Gson gson = new GsonBuilder()
            .registerTypeAdapter(byte[].class, new Base64UrlAdapter())
            .create();

    // 次のデータ構造を表す Map インスタンスを作成する
    //
    //   {
    //     "binary": [ 18, 52, 86 ]
    //   }
    //
    Map<String, Object> input = new LinkedHashMap<>();
    input.put("binary", new byte[]{ (byte)0x12, (byte)0x34, (byte)0x56 });

    // JSON へと変換する
    String json = gson.toJson(input);

    // JSON の内容は {"binary":"EjRW"} になっているはず
    assertEquals(json, "{\"binary\":\"EjRW\"}");
}
base64url 文字列をバイト配列へデシリアライズする
@Test
public void test_from_base64url()
{
    // Base64UrlAdapter を有効にして Gson を作成する
    Gson gson = new GsonBuilder()
            .registerTypeAdapter(byte[].class, new Base64UrlAdapter())
            .create();

    // {"binary":"EjRW"} という内容を持つ JSON を用意する
    String input = "{\"binary\":\"EjRW\"}";

    // JSON をデシリアライズして Data クラスのインスタンスを得る
    Data data = gson.fromJson(input, Data.class);

    // バイト配列 data.binary の長さが 3 で、その要素の値が先頭から
    // (byte)0x12, (byte)0x34, (byte)0x56 であることを確認する
    assertNotNull(data.binary);
    assertEquals(3, data.binary.length);
    assertEquals((byte)0x12, data.binary[0]);
    assertEquals((byte)0x34, data.binary[1]);
    assertEquals((byte)0x56, data.binary[2]);
}


private static class Data
{
    byte[] binary;
}

おわりに

Token Status List という仕様の StatusList 構造の lst プロパティの値は、CBOR フォーマットではバイト配列ですが、JSON フォーマットでは base64url 文字列となっています。このようなデータ構造を扱うときに、今回作成した Base64UrlAdapter を使うことがあるかもしれません。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?