1. 概要

この前回の記事では、BSONドキュメントをMongoDBからJavaオブジェクトとして取得する方法を見てきました。

これは、REST APIを開発するための非常に一般的な方法です。これらのオブジェクトをJSONに変換する前に変更したい場合があるためです(たとえば、 Jackson を使用)。

ただし、ドキュメントに何も変更したくない場合があります。 冗長なJavaオブジェクトマッピングをコーディングする手間を省くために、直接BSONからJSONドキュメントへの変換を使用できます。

このユースケースでMongoDBBSONAPIがどのように機能するかを見てみましょう。

2. Morphiaを使用したMongoDBでのBSONドキュメントの作成

まず、この記事で説明されているのようにMorphiaを使用して依存関係を設定しましょう。

さまざまな属性タイプを含む例エンティティを次に示します。

@Entity("Books")
public class Book {
    @Id
    private String isbn;

    @Embedded
    private Publisher publisher;

    @Property("price")
    private double cost;

    @Property
    private LocalDateTime publishDate;

    // Getters and setters ...
}

次に、テスト用の新しいBSONエンティティを作成し、MongoDBに保存しましょう。

public class BsonToJsonIntegrationTest {
    
    private static final String DB_NAME = "library";
    private static Datastore datastore;

    @BeforeClass
    public static void setUp() {
        Morphia morphia = new Morphia();
        morphia.mapPackage("com.baeldung.morphia");
        datastore = morphia.createDatastore(new MongoClient(), DB_NAME);
        datastore.ensureIndexes();
        
        datastore.save(new Book()
          .setIsbn("isbn")
          .setCost(3.95)
          .setPublisher(new Publisher(new ObjectId("fffffffffffffffffffffffa"),"publisher"))
          .setPublishDate(LocalDateTime.parse("2020-01-01T18:13:32Z", DateTimeFormatter.ISO_DATE_TIME)));
    }
}

3. デフォルトのBSONからJSONドキュメントへの変換

次に、非常に単純なデフォルトの変換をテストしましょう。BSONドキュメントクラスからtoJsonメソッドを呼び出すだけです。

@Test
public void givenBsonDocument_whenUsingStandardJsonTransformation_thenJsonDateIsObjectEpochTime() {
     String json = null;
     try (MongoClient mongoClient = new MongoClient()) {
         MongoDatabase mongoDatabase = mongoClient.getDatabase(DB_NAME);
         Document bson = mongoDatabase.getCollection("Books").find().first();
         assertEquals(expectedJson, bson.toJson());
     }
}

expectedJsonの値は次のとおりです。

{
    "_id": "isbn",
    "className": "com.baeldung.morphia.domain.Book",
    "publisher": {
        "_id": {
            "$oid": "fffffffffffffffffffffffa"
        },
        "name": "publisher"
    },
    "price": 3.95,
    "publishDate": {
        "$date": 1577898812000
    }
}

これは、標準のJSONマッピングに対応しているようです。

しかし、私たちはそれを見ることができます日付は、デフォルトで、エポック時間形式の$dateフィールドを持つオブジェクトとして変換されました。 この日付形式を変更する方法を見てみましょう。

4. リラックスしたBSONからJSONへの日付変換

たとえば、より古典的なISO日付表現(JavaScriptクライアントなど)が必要な場合は、JsonWriterSettings.builder を使用して、リラックスしたJSONモードをtoJsonメソッドに渡すことができます。

bson.toJson(JsonWriterSettings
  .builder()
  .outputMode(JsonMode.RELAXED)
  .build());

その結果、publishDateフィールドの「リラックスした」変換を確認できます。

{
    ...
    "publishDate": {
        "$date": "2020-01-01T17:13:32Z"
    }
    ...
}

この形式は正しいように見えますが、まだ $ date フィールドがあります—カスタムコンバーターを使用してそれを取り除く方法を見てみましょう。

5. カスタムBSONからJSONへの日付変換

まず、日付値はエポック時間からのミリ秒で表されるため、タイプLongBSONコンバーターインターフェイスを実装する必要があります。 DateTimeFormatter.ISO_INSTANT を使用して、期待される出力形式を取得しています。

public class JsonDateTimeConverter implements Converter<Long> {

    private static final Logger LOGGER = LoggerFactory.getLogger(JsonDateTimeConverter.class);
    static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ISO_INSTANT
        .withZone(ZoneId.of("UTC"));

    @Override
    public void convert(Long value, StrictJsonWriter writer) {
        try {
            Instant instant = new Date(value).toInstant();
            String s = DATE_TIME_FORMATTER.format(instant);
            writer.writeString(s);
        } catch (Exception e) {
            LOGGER.error(String.format("Fail to convert offset %d to JSON date", value), e);
        }
    }
}

次に、このクラスのインスタンスをDateTimeコンバーターとしてJsonWriterSettingsビルダーに渡すことができます。

bson.toJson(JsonWriterSettings
  .builder()
  .dateTimeConverter(new JsonDateTimeConverter())
  .build());

最後に、プレーンなJSONISO日付形式を取得します。

{
    ...
    "publishDate": "2020-01-01T17:13:32Z"
    ...
}

6. 結論

この記事では、BSONからJSONドキュメントへの変換のデフォルトの動作を確認しました。

BSON Converter を使用して、一般的な問題である日付形式をカスタマイズする方法を強調しました。

もちろん、同じ方法で他のデータ型を変換することもできます。たとえば、数値、ブール値、null値、オブジェクトIDなどです。

いつものように、コードはGitHubにあります。