[Dart] クラスでインスタンス化したオブジェクトをデバッグ出力したい

2023.10.21

こんにちは、CX 事業本部 Delivery 部の若槻です。

Flutter では Dart 言語を使ってアプリ開発を行います。

今回はこの Dart の実装で、クラスでインスタンス化したオブジェクトを、デバッグ出力する方法を確認してみました。

print だとインスタンスを文字列出力できない

Dart では print function を使えば、オブジェクトを文字列として出力できます。

しかし、クラスでインスタンス化したオブジェクトを print すると、Instance of 'ClassName' という文字列で出力されてしまいます。

main.dart

void main() {
  final collection1 = List.from([1, 2, 3]);
  print(collection1);

  final collection2 = List.from(['foo', 'bar', 'dart']);
  print(collection2);

  final object = {'a': 1, 'b': 2};
  print(object);

  final data = DataModel(
    id: '1',
    count: 10,
    isActive: true,
  );
  print(data);
}

class DataModel {
  final String id;
  final int count;
  final bool isActive;

  DataModel({
    required this.id,
    required this.count,
    required this.isActive,
  });
}

$ dart main.dart
[1, 2, 3]
[foo, bar, dart]
{a: 1, b: 2}
Instance of 'DataModel'

インスタンスを文字列出力する方法

次のようにクラスでインスタンスをオブジェクトに変換するメソッドを実装し、print 使用時に使用すると文字列出力できるようになります。

main.dart

void main() {
  final data = DataModel(
    id: '1',
    count: 10,
    isActive: true,
  );
  print(data.toJson());
}

class DataModel {
  final String id;
  final int count;
  final bool isActive;

  DataModel({
    required this.id,
    required this.count,
    required this.isActive,
  });

  Map<String, dynamic> toJson() {
    var data = {
      'id': id,
      'count': count,
      'isActive': isActive,
    };

    return data;
  }
}

$ dart main.dart
{id: 1, count: 10, isActive: true}

convert ではデフォルトで toJson が呼び出される

JsonEncoder の convert method を使ってもオブジェクトを文字列出力できますが、今回作成したようなインスタンスを出力したい場合にもまた toJson メソッドを実装する必要があります。

main.dart

import 'dart:convert';

void main() {
  const jsonEncoder = JsonEncoder();

  final data = DataModel(
    id: '1',
    count: 10,
    isActive: true,
  );
  print(jsonEncoder.convert(data));
}

class DataModel {
  final String id;
  final int count;
  final bool isActive;

  DataModel({
    required this.id,
    required this.count,
    required this.isActive,
  });

  Map<String, dynamic> toJson() {
    var data = {
      'id': id,
      'count': count,
      'isActive': isActive,
    };

    return data;
  }
}

$ dart main.dart
{id: 1, count: 10, isActive: true}

インスタンスの生成元のクラスに toJson が未実装の場合は次のようにエラーになります。

$ dart main.dart
Unhandled exception:
Converting object to an encodable object failed: Instance of 'DataModel'
#0      _JsonStringifier.writeObject (dart:convert/json.dart:793:7)
#1      _JsonStringStringifier.printOn (dart:convert/json.dart:982:17)
#2      _JsonStringStringifier.stringify (dart:convert/json.dart:967:5)
#3      JsonEncoder.convert (dart:convert/json.dart:345:30)
#4      main (file:///Users/wakatsuki.ryuta/projects/cm-rwakatsuki/my_flutter_app/main_3.dart:11:21)
#5      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#6      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:192:26)

ソースコードを見ると、convert ではデフォルトで toJson が呼び出されるとあります。

/Users/wakatsuki.ryuta/packages/flutter/bin/cache/pkg/sky_engine/lib/convert/json.dart

  /// Create converter.
  ///
  /// If [indent] is non-`null`, the converter attempts to "pretty-print" the
  /// JSON, and uses `indent` as the indentation. Otherwise the result has no
  /// whitespace outside of string literals.
  /// If `indent` contains characters that are not valid JSON whitespace
  /// characters, the result will not be valid JSON. JSON whitespace characters
  /// are space (U+0020), tab (U+0009), line feed (U+000a) and carriage return
  /// (U+000d) ([ECMA
  /// 404](http://www.ecma-international.org/publications/standards/Ecma-404.htm)).
  ///
  /// The [bufferSize] is the size of the internal buffers used to collect
  /// UTF-8 code units.
  /// If using [startChunkedConversion], it will be the size of the chunks.
  ///
  /// The JSON encoder handles numbers, strings, booleans, null, lists and maps
  /// directly.
  ///
  /// Any other object is attempted converted by [toEncodable] to an object that
  /// is of one of the convertible types.
  ///
  /// If [toEncodable] is omitted, it defaults to calling `.toJson()` on the
  /// object.
  JsonUtf8Encoder(
      [String? indent, dynamic toEncodable(dynamic object)?, int? bufferSize])
      : _indent = _utf8Encode(indent),
        _toEncodable = toEncodable,
        _bufferSize = bufferSize ?? _defaultBufferSize;

おわりに

Dart で、クラスでインスタンス化したオブジェクトをデバッグ出力する方法を確認してみました。

クラスを利用する場合はインスタンスを文字列出力できるように toJson メソッドを実装しておくと良さそうですね。

参考

以上