LoginSignup
5
6

More than 3 years have passed since last update.

jackson-databind(java)のObjectMapperでバイナリを扱う際のエンコーディング指定方法

Posted at

「jackson-databind」とは、JSONとjavaのオブジェクト間の相互変換等のために利用されるjavaのライブラリです。
「ObjectMapper」(ObjectMapper.java)とは「jackson-databind」において上記の機能を提供するクラスです。
(以降、簡単に「jackson」や「ObjectMapper」と表記します)

jacksonやObjectMapperの基本的な使い方に関しては、他の方の記事やjackson-databindのGitHubのドキュメントを参照ください(*1)。

当記事では、

  • バイナリデータが与えられる(例:Httpリクエストのリクエストボディ、外部ファイルの入力)
  • バイナリデータを出力する(例:Httpレスポンスのレスポンスボディ、外部ファイル書き込み)

場合の、ObjectMapperに対するエンコーディングの指定方法を紹介します。

また、ObjectMapperの動作の背景にあるJSONのエンコーディングの考え方についても言及します。

「エンコーディングの指定方法」は、ざっくり述べると「UTF-8(+α)以外の場合、Reader/Writerを渡すメソッドを使用する」という単純明快なものです。

動作確認した環境やVersion

  • jackson-databind 2.9.8 (2019/5/3時点最新版)
  • jdk11

デモ用ソースコード

重要なポイントは以下の4点です。以降で(a)~(d)をそれぞれ詳しく見ていきます。
ソースコードはGitHubにもアップロードしています。

  • (a). UTF-8, UTF-16, UTF-32を「read」する場合は、エンコーディングの指定は不要 (byte[]やInputStreamを引数に渡すタイプのメソッドを使用可能)
  • (b). UTF-8, UTF-16, UTF-32以外を「read」する場合は、エンコーディングを明示したReaderを生成し、Readerを引数に渡すタイプのメソッドを使用する
  • (c). UTF-8で「write」する場合は、エンコーディングの指定は不要(byte[]やInputStreamを引数に渡すタイプのメソッドを使用可能)
  • (d). UTF-8以外で「write」する場合は、エンコーディングを明示したWriterを生成し、Writerを引数に渡すタイプのメソッドを使用する
この記事で使用する文言の補足
  • 「read」: 「JSONのバイナリデータ to javaオブジェクト」の変換
  • 「write」: 「javaオブジェクト to JSONのバイナリデータ」の変換
調査対象としたObjectMapperのメソッド
  • 「read」のメソッド
    • 「readValue」の内、第一引数がStringでないもの
    • 「readTree」の内、第一引数がStringでないもの
  • 「write」のメソッド
    • 「wrteValue」
    • 「writeTree」
    • 「writeValueAsBytes」

「DataOutput」「DataInput」を引数に取るメソッドは他と比べて若干特殊なため「Appendix」で紹介します。

上記のメソッドは様々な引数を取れるようにオーバーロードされています。
対象のメソッドが多いため、対象のすべてのメソッドについてサンプルソースを掲載しているわけではありません。

準備

以降のソースコードで以下の変数やクラスを共通で使用します。

なお、以降のソースコードではリソースのクローズ処理が必要な個所でも一律割愛しています。


    ObjectMapper mapper = new ObjectMapper();

    String json = "{\"message\":\"あいうえお\"}"; // 「read」するJSON
    String readExpected = new Bean("あいうえお").toString();// 「read」の期待値

    Bean bean = new Bean("かきくけこ");// 「write」するjavaのオブジェクト
    String writeExpected = "{\"message\":\"かきくけこ\"}";// 「write」の期待値

    static class Bean {
        private String message;
        //setter ,getter, コンストラクタ, は割愛
        public String toString() {
            return "Bean [message=" + message + "]";
        }
    }

詳細

(a). UTF-8, UTF-16, UTF-32を「read」する場合は、エンコーディングの指定は不要

UTF-8, UTF-16, UTF-32を「read」する場合は、エンコーディングが自動で判定されるため、
byte[]やInputStreamを引数に渡すタイプのメソッドを使用可能です。


    /*
     * (a) UTF-8, UTF-16, UTF-32を「read」する場合は、エンコーディングの指定は不要
     * 
     * String#getBytesを使用し、任意のエンコーディングのバイトデータが与えられた状況をシミュレートしています
     *  確認に使用しているメソッド :
     * ObjectMapper#readValue(byte[], Class<T>)<T> : T
     */
    @Test
    public void readValueFromUTF8or16or32ByteArrayShouldDetectEncodingSuccessfully() throws Exception {

        Bean beanFromUTF32BE = mapper.readValue(json.getBytes("UTF-32BE"), Bean.class);
        Bean beanFromUTF16BE = mapper.readValue(json.getBytes("UTF-16BE"), Bean.class);
        Bean beanFromUTF32LE = mapper.readValue(json.getBytes("UTF-32LE"), Bean.class);
        Bean beanFromUTF16LE = mapper.readValue(json.getBytes("UTF-16LE"), Bean.class);
        Bean beanFromUTF8 = mapper.readValue(json.getBytes("UTF-8"), Bean.class);

        assertThat(beanFromUTF32BE.toString(), equalTo(readExpected));
        assertThat(beanFromUTF16BE.toString(), equalTo(readExpected));
        assertThat(beanFromUTF32LE.toString(), equalTo(readExpected));
        assertThat(beanFromUTF16LE.toString(), equalTo(readExpected));
        assertThat(beanFromUTF8.toString(), equalTo(readExpected));

        // !! 以下を実行するとExceptionが発生します。
        // mapper.readValue(json.getBytes("Shift_JIS"), Bean.class);
        // -> 「com.fasterxml.jackson.databind.JsonMappingException: Invalid UTF-8 start byte 0x82」

    }

    /*
     * (a) 上記の「 ObjectMapper#readValue(byte[], Class<T>)<T> : T 」以外の
     * 「(a)」パターンとして利用できるメソッド
     */
    @Test
    public void readMethodsFromAnyUTF8BinaryDataShouldDetectEncodingSuccessfully() throws Exception {

        File file = new File("sample_UTF-8.json");
        // 第一引数 : URL
        Bean beanFromURL = mapper.readValue(file.toURI().toURL(), Bean.class);
        assertThat(beanFromURL.toString(), equalTo(readExpected));

        // 第一引数 : File
        Bean beanFromFile = mapper.readValue(file, Bean.class);
        assertThat(beanFromFile.toString(), equalTo(readExpected));

        // 第一引数 : InputStream
        InputStream inputStream = new FileInputStream(file);
        Bean beanFromInputStream = mapper.readValue(inputStream, Bean.class);
        assertThat(beanFromInputStream.toString(), equalTo(readExpected));

        // 第一引数: JsonParser
        // 「createParser」に渡せる引数の種類は、「readValue」の第一引数と似ています
        // 以下は、byte[]を渡す例です。
        // 他にも、File, URL, InputStream, Readerを渡すメソッド等があります
        JsonParser jsonParser = mapper.getFactory().createParser(json.getBytes("UTF-8"));
        Bean beanFromJsonParser = mapper.readValue(jsonParser, Bean.class);
        assertThat(beanFromJsonParser.toString(), equalTo(readExpected));

        // 「readTree」の場合も、「readValue」と同じような引数を渡せます。
        // 以下は、byte[]を渡す例です。
        JsonNode jsonNode = mapper.readTree(json.getBytes("UTF-8"));
        assertThat(jsonNode.get("message").asText(), equalTo("あいうえお"));
    }

背景にある(と想定される)JSONの仕様について

最新のJSONの仕様(RFC 8259)では、クローズドなエコシステム以外では「UTF-8」の使用が必須とされています。
一方、現時点のjacksonはRFC 4627を念頭に実装されているようです(*2)。

RFC 4627の「3. Encoding」よると、JSONテキストの先頭2文字は常にASCII文字であるため、JSONテキストの先頭の4バイト(*3)における「00」のパターンにより、「UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE)」が判断できます。
デモ用ソースコードで使用しているJSONテキストにて確認してみます。

JSONテキスト:{"message": "あいうえお"}(先頭2文字は「{"」)

encoding 先頭4バイト
UTF-32BE 00 00 00 7B
UTF-16BE 00 7B 00 22
UTF-32LE 7B 00 00 00
UTF-16LE 7B 00 22 00
UTF-8 7B 22 6D 65

先頭2バイトの両方が「00」以外であるのはUTF-8のみ、先頭2バイト両方が「00」なのはUTF-32BEのみ・・・・・といった要領でencodingが判定できることがわかります。

動作確認の結果、および以上の理解に基づくならば、UTF-8, UTF-16, UTF-32でエンコードされたバイトデータを「read」する場合には、エンコーディングの指定は不要といえると思われます。

(b). UTF-8, UTF-16, UTF-32以外を「read」する場合は、エンコーディングを明示したReaderを生成し、Readerを引数に渡すタイプのメソッドを使用する

jacksonのjavadoc(*4)にも記載されている通り、UTF-8, UTF-16, UTF-32以外のエンコーディングを使用する場合には、エンコーディングを明示したReaderを生成し、Readerを引数に渡すタイプのメソッドを使用します。

「(a)」に記載したように、jacksonがRFC 4627を念頭に置いているならば、UTF-8, UTF-16, UTF-32以外のエンコーディングを自動判定できない(しない)のは自然な動作です(*5)。



    /*
     * (b) UTF-8, UTF-16, UTF-32以外を「read」する場合は、
     * エンコーディングを明示したReaderを生成し、Readerを引数に渡すタイプのメソッドを使用する
     * 
     * String#getBytesを使用し、任意のエンコーディングのバイトデータが与えられた状況をシミュレートしています 
     * 確認に使用しているメソッド :
     * ObjectMapper#readValue(Reader, Class<T>)<T> : T
     */
    @Test
    public void readValueFromReaderShouldReadBytesOfSpecifiedEncoding() throws Exception {

        // 現実のアプリケーションの例:
        // 「InputStream」を HttpServletRequest#getInputStreamから取得する
        InputStream inputStream = new ByteArrayInputStream(json.getBytes("Shift_JIS"));
        Reader reader = new InputStreamReader(inputStream, "Shift_JIS");
        Bean beanFromReader = mapper.readValue(reader, Bean.class);
        assertThat(beanFromReader.toString(), equalTo(readExpected));
    }

    /*
     * (b) 上記の「 ObjectMapper#readValue(Reader, Class<T>)<T> : T 」以外の
     * 「(b)」パターンとして利用できるメソッド
     */
    @Test
    public void readMethodsFromReaderShouldReadBytesOfSpecifiedEncoding() throws Exception {

        // 第一引数: JsonParser
        // 「createParser」にReaderを渡す
        Reader reader = new InputStreamReader(new ByteArrayInputStream(json.getBytes("Shift_JIS")), "Shift_JIS");
        JsonParser jsonParser = mapper.getFactory().createParser(reader);
        Bean beanFromJsonParser = mapper.readValue(jsonParser, Bean.class);
        assertThat(beanFromJsonParser.toString(), equalTo(readExpected));

        // 「readTree」でもReaderを渡す
        Reader readerToTree = new InputStreamReader(new ByteArrayInputStream(json.getBytes("Shift_JIS")), "Shift_JIS");
        JsonNode jsonNode = mapper.readTree(readerToTree);
        assertThat(jsonNode.get("message").asText(), equalTo("あいうえお"));
    }

(c). UTF-8で「write」する場合は、エンコーディングの指定は不要

エンコーディングの指定がないタイプの「write」するメソッドでは、エンコーディングはUTF-8となっています(*6)。
そのため、書き出すバイトデータのエンコーディングがUTF-8の場合、エンコーディングの明示は不要です。


    /*
     * (c) UTF-8で「write」する場合は、エンコーディングの指定は不要
     * 
     * ByteArrayOutputStreamを使用し、OutputStreamにJSONを書き出す状況をシミュレートしています 
     * 確認に使用しているメソッド
     * : ObjectMapper#writeValue(OutputStream, Object) : void
     */
    @Test
    public void writeValueNotSpecifyingEncodingShouldWriteUTF8Bytes() throws Exception {

        // 現実のアプリケーションの例:
        // 「OutputStream」を HttpServletResponse#getOutputStreamから取得する
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        mapper.writeValue(outputStream, bean);
        assertThat(outputStream.toByteArray(), equalTo(writeExpected.getBytes("UTF-8")));
    }

    /*
     * (c) 上記の「 ObjectMapper#writeValue(OutputStream, Object) : void 」以外の
     * 「(c)」パターンとして利用できるメソッド
     */
    @Test
    public void writeMethodsNotSpecifyingEncodingShouldWriteUTF8Bytes() throws Exception {

        // 第一引数 : File
        File outFile = new File("out_sample.json");
        mapper.writeValue(outFile, bean);
        byte[] fileData = Files.readAllBytes(outFile.toPath());
        assertThat(fileData, equalTo(writeExpected.getBytes("UTF-8")));

        // 第一引数: JsonGenerator
        // 「createGenerator」に渡せる引数の種類は、「writeValue」の第一引数と似ています。
        // 以下はOutputStreamを渡す例です。
        // 他にもFileを渡すメソッド等があります。
        ByteArrayOutputStream jsonGeneratorOutputBytes = new ByteArrayOutputStream();
        JsonGenerator jsonGenerator = mapper.getFactory().createGenerator(jsonGeneratorOutputBytes);
        mapper.writeValue(jsonGenerator, bean);
        assertThat(jsonGeneratorOutputBytes.toByteArray(), equalTo(writeExpected.getBytes("UTF-8")));

        // 「writeValueAsBytes」
        assertThat(mapper.writeValueAsBytes(bean), equalTo(writeExpected.getBytes("UTF-8")));

        // 「writeTree」の第一引数は、「JsonGenerator」のため、
        // ことエンコーディングに関しては「writeValue」において「JsonGenerator」を使用する時と同じ要領で使用します
        ByteArrayOutputStream jsonGeneratorOutputBytesForTree = new ByteArrayOutputStream();
        JsonGenerator jsonGeneratorForTree = mapper.getFactory().createGenerator(jsonGeneratorOutputBytesForTree);
        JsonNode tree = mapper.readTree(writeExpected);
        mapper.writeTree(jsonGeneratorForTree, tree);
        assertThat(jsonGeneratorOutputBytesForTree.toByteArray(), equalTo(writeExpected.getBytes("UTF-8")));
    }

    /*
     * (c)補足 JsonGeneratorを使用する場合に関する補足
     * 「createGenerator」の引数に「JsonEncoding」を渡せる場合は、UTF-16, UTF-32も使用できます
     */
    @Test
    public void writeValueByJsonGeneratorCanAlsoSpecifyUTF16orUTF32Encoding() throws Exception {

        ByteArrayOutputStream jsonGeneratorOutputBytes = new ByteArrayOutputStream();
        JsonGenerator jsonGenerator = mapper.getFactory().createGenerator(jsonGeneratorOutputBytes,
                JsonEncoding.UTF16_BE);
        mapper.writeValue(jsonGenerator, bean);

        assertThat(jsonGeneratorOutputBytes.toByteArray(), equalTo(writeExpected.getBytes("UTF-16BE")));
    }   

(d). UTF-8以外で「write」する場合は、エンコーディングを明示したWriterを生成し、Writerを引数に渡すタイプのメソッドを使用する

エンコーディングを明示したい場合は、jacksonのjavadoc(*5)にも記載されている通り、エンコーディングを明示したWriterを生成し、Writerを引数に渡すタイプのメソッドを使用します。

    /*
     * (d) UTF-8以外で「write」する場合は、 エンコーディングを明示したWriterを生成し、
     * Writerを引数に渡すタイプのメソッドを使用する
     * 
     * ByteArrayOutputStreamを使用し、OutputStreamにJSONを書き出す状況をシミュレートしています 
     * 確認に使用しているメソッド
     * : ObjectMapper#writeValue(Writer, Object) : void
     */
    @Test
    public void writeValueFromWriterShouldWriteBytesOfSpecifiedEncoding() throws Exception {

        // 現実のアプリケーションの例:
        // 「OutputStream」を HttpServletResponse#getOutputStreamから取得する
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        OutputStreamWriter writer = new OutputStreamWriter(outputStream, "Shift_JIS");
        mapper.writeValue(writer, bean);

        assertThat(outputStream.toByteArray(), equalTo(writeExpected.getBytes("Shift_JIS")));
    }

    /*
     * (d) 上記の「 ObjectMapper#writeValue(Writer, Object) : void 」以外の
     * 「(d)」パターンとして利用できるメソッド
     */
    @Test
    public void writeMethodsFromWriterShouldWriteBytesOfSpecifiedEncoding() throws Exception {

        // 第一引数: JsonGenerator
        // 「createGenerator」にWriterを渡す
        ByteArrayOutputStream jsonGeneratorOutputBytes = new ByteArrayOutputStream();
        OutputStreamWriter writer = new OutputStreamWriter(jsonGeneratorOutputBytes, "Shift_JIS");
        JsonGenerator jsonGenerator = mapper.getFactory().createGenerator(writer);
        mapper.writeValue(jsonGenerator, bean);
        assertThat(jsonGeneratorOutputBytes.toByteArray(), equalTo(writeExpected.getBytes("Shift_JIS")));

        // 「writeTree」の第一引数は、「JsonGenerator」のため、
        // ことエンコーディングに関しては「writeValue」において「JsonGenerator」を使用する時と同じ要領で使用します
        ByteArrayOutputStream jsonGeneratorOutputBytesForTree = new ByteArrayOutputStream();
        OutputStreamWriter writerForTree = new OutputStreamWriter(jsonGeneratorOutputBytesForTree, "Shift_JIS");
        JsonGenerator jsonGeneratorForTree = mapper.getFactory().createGenerator(writerForTree);
        JsonNode tree = mapper.readTree(writeExpected);
        mapper.writeTree(jsonGeneratorForTree, tree);
        assertThat(jsonGeneratorOutputBytesForTree.toByteArray(), equalTo(writeExpected.getBytes("Shift_JIS")));
    }

Appendix

1.「DataOutput」「DataInput」を引数に取るメソッド

  • 「DataOutput」とは
    • プリミティブ型や文字列をバイトデータとして書き出す機能を提供
    • 文字列は「modified UTF-8」という形式で出力される(*7)
  • 「DataInput」とは
    • DataOutput等で書き出されたバイトデータをプリミティブ型や文字列として読み出す機能を提供

まずは、一旦jacksonを離れて、「DataOutput」「DataInput」そのものの使用方法を確認します。
以下のように使用します(*8)。


    /*
     * DataInput / DataOutputの典型的な使用例 
     * ・DataOutput : プリミティブ型または文字列を直接バイトデータとして書き出すために使用する
     * ・DataInput : DataOutput等で書き出されたバイトデータを読み出すために使用する
     */
    @Test
    public void personShouldBeSerializedByDataOutputAndDeserializedByDataInput() throws Exception {

        Person person = new Person();
        person.setAge(25);
        person.setName("𠮷太郎");
        person.setKanaCode('あ');
        File file = new File("sample_dataouput.dat");

        // DataOutputによるバイトデータの書き出し
        DataOutput dout = new DataOutputStream(new FileOutputStream(file));
        dout.writeInt(person.getAge());
        dout.writeUTF(person.getName());// 「modified UTF-8」で出力
        dout.writeChar(person.getKanaCode());

        // DataInputによるバイトデータの読み出し
        DataInput din = new DataInputStream(new FileInputStream(file));
        Person deserializedPerson = new Person();
        deserializedPerson.setAge(din.readInt());
        deserializedPerson.setName(din.readUTF());// 「modified UTF-8」として読み出す
        deserializedPerson.setKanaCode(din.readChar());

        assertThat(deserializedPerson.toString(), equalTo(person.toString()));
    }

jacksonにおける想定される使用方法については、公式のドキュメント等に記載は見つけられませんでした。
しかし、上記コードからの類推でいろいろと試した結果、下記のコードで想定した結果が得られました。

結論としては、以下のように使用すればよさそうです

  • 「writeValue(DataOutput, ...)」でシリアライズしたなら、「readValue(DataInput, ...)」でデシリアライズ
  • 「writeUTF」で書き出したなら、「readUTF」で読み出した後、「readValue(String, ...)」等でデシリアライズ

    /*
     * ObjectMapperで、DataInput / DataOutputを使用する例
     */
    @Test
    public void person2ShouldBeSerializedByDataOutputAndDeserializedByDataInput() throws Exception {

        Person2 person = new Person2();
        person.setAge(25);
        PersonName personName = new PersonName();
        personName.setFirstname("𠮷太郎");
        personName.setLastname("山田");
        person.setPersonName(personName);
        person.setKanaCode('あ');
        File file = new File("sample_dataouput2.dat");

        // DataOutputによるバイトデータの書き出し
        DataOutput dout = new DataOutputStream(new FileOutputStream(file));
        dout.writeInt(person.getAge());
        // PersonNameはJSONで書き出す
        mapper.writeValue(dout, person.getPersonName());
        dout.writeChar(person.getKanaCode());

        // DataInputによるバイトデータの読み出し
        DataInput din = new DataInputStream(new FileInputStream(file));
        Person2 deserializedPerson = new Person2();
        deserializedPerson.setAge(din.readInt());
        PersonName deserializedPersonName = mapper.readValue(din, PersonName.class);
        // 以下のように記述しても正しくDeserializeできませんでした
        // PersonName deserializedPersonName = mapper.readValue(din.readUTF(),
        // PersonName.class);
        deserializedPerson.setPersonName(deserializedPersonName);
        deserializedPerson.setKanaCode(din.readChar());

        assertThat(deserializedPerson.toString(), equalTo(person.toString()));
    }

    /*
     * ObjectMapperで、DataInput / DataOutputを使用する場合の例 
     * 「writeUTF」で書き出されたJSONは
     *  ObjectMapper#readValue(DataInput, Class<T>) <T>: T
     * では正しくDeserializeできない !!! このテストは失敗します
     */
    @Test
    public void modifiedUTF8StringShouldBeDeserializedByDataInput() throws Exception {

        File file = new File("sample_dataouput3.dat");

        // DataOutputによるバイトデータの書き出し
        DataOutput dout = new DataOutputStream(new FileOutputStream(file));
        dout.writeInt(55);
        dout.writeUTF("{\"firstname\":\"𠮷太郎\", \"lastname\":\"山田\"}");
        dout.writeChar('あ');

        // DataInputによるバイトデータの読み出し
        DataInput din = new DataInputStream(new FileInputStream(file));
        assertThat(55, equalTo(din.readInt()));
        PersonName deserializedPersonName = mapper.readValue(din, PersonName.class);
        // 以下のようにすると正しくDeserializeされました
        // PersonName deserializedPersonName = mapper.readValue(din.readUTF(),
        // PersonName.class);
        assertThat(deserializedPersonName.getFirstname(), equalTo("𠮷太郎"));
        assertThat(deserializedPersonName.getLastname(), equalTo("山田"));
        assertThat(din.readChar(), equalTo('あ'));
    }
    // 「1.「DataOutput」「DataInput」を引数に取るメソッド」のサンプルソースで使用するクラス
    // setter, getter, toStringは割愛。toStringの結果の比較でフィールド値が比較できるようになっています。
    static class Person2 {
        private int age;
        private PersonName personName;
        private char kanaCode;
    }

    static class PersonName {
        private String firstname;
        private String lastname;
    }

    static class Person {
        private int age;
        private String name;
        private char kanaCode;
    }

2. JsonParser, JsonGeneratorについての補足

「JsonParser」「JsonGenerator」は当記事で対象としている「jackson-databind」ではなく「jackson-core」に位置付けられているクラスです(*9)。
「jackson-core」には(その名の通り)コアな低レベルのparser/generatorが含まれており、「jackson-databind」は「jackson-core」に依存しています。
コアな低レベルのparser/generatorが、ここで紹介する「JsonParser」「JsonGenerator」に当たります。

記事本文ではObjectMapperのメソッドに対象を絞っていたため、「JsonParser」「JsonGenerator」自体の使い方は紹介していませんでした。
以下で、エンコーディングを指定して「JsonParser」「JsonGenerator」を使用する例を紹介します。


    /*
     * JsonParserに関する補足 
     * UTF-8以外のエンコーディングを使用し、 
     * 典型的なJsonParserの使い方でJSONをreadする例です
     */
    @Test
    public void jsonParserShouldReadJSONIncrementally() throws Exception {

        Reader reader = new InputStreamReader(new ByteArrayInputStream(json.getBytes("Shift_JIS")), "Shift_JIS");
        JsonParser jsonParser = mapper.getFactory().createParser(reader);

        JsonToken t = null;
        String fieldName = null;
        String message = null;
        while ((t = jsonParser.nextToken()) != null) {
            if (t == JsonToken.FIELD_NAME) {
                fieldName = jsonParser.getCurrentName();
            }
            if (t == JsonToken.VALUE_STRING) {
                message = jsonParser.getText();
            }
        }
        assertThat(fieldName, equalTo("message"));
        assertThat(message, equalTo("あいうえお"));
    }

    /*
     * JsonGeneratorに関する補足
     * UTF-8以外のエンコーディングを使用し、
     * 典型的なJsonGeneratorの使い方でJSONをwriteする例です。
     */
    @Test
    public void jsonGeneratorShouldWriteJSONIncrementally() throws Exception {

        ByteArrayOutputStream outputBytes = new ByteArrayOutputStream();
        Writer writer = new OutputStreamWriter(outputBytes, "Shift_JIS");
        JsonGenerator jsonGenerator = mapper.getFactory().createGenerator(writer);
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("message", "あいうえお");
        jsonGenerator.writeEndObject();
        jsonGenerator.close();

        assertThat(outputBytes.toByteArray(), equalTo("{\"message\":\"あいうえお\"}".getBytes("Shift_JIS")));
    }

注釈および参考サイト

RFC 4627(2019/5/3閲覧)
RFC 8259(2019/5/3閲覧)

*1
jacksonの使い方については、少し昔の記事ですが、Jackson使い方メモ(2019/5/3閲覧)が参考になります。
jackson-databindのGitHubにおける「Use It!」(2019/5/3閲覧)も参考になります。

*2
実際に動作させて確認した結果および、「JsonEncoding.java」(2019/5/3閲覧)のjavadocの記述を根拠としています。

*3
RFC 4627の「3. Encoding」にはBOMに関する記述はありませんが、BOMは無視して判断する前提で記述されていると考えられます。

*4
JsonEncoding.java」(2019/5/3閲覧)に以下の記述があります。
「 Note: if application want to explicitly disregard Encoding limitations (to read in JSON encoded using an encoding not listed as allowed), they can use {@link java.io.Reader} / {@link java.io.Writer} instances as input」

*5
デモ用ソースコードの「(a)」の部分にも記載されている通り、Shift_JIS等でエンコードされたデータを「read」するとエラーとなる場合があります。

*6
「ObjectMapper#writeValue(OutputStream, Object): void」のjavadocには明記されています

*7
DataInput」(2019/5/4閲覧)のjavadoc参照。

*8
KAB-studio > プログラミング > JavaA2Z > DataInputStream」(2019/5/4閲覧)を参考にしました。

*9
jackson-core」(2019/5/4閲覧)のGitHubの記述を参照しました。

5
6
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
5
6