Chapter 04

Stringをenumに変換したい

こんぶ
こんぶ
2022.04.04に更新

A. byName を使いましょう

ある Web API からの次のような JSON レスポンスが返ってきました。

{
  "name": "こんぶ",
  "job": "engineer"
}

バックエンドのエンジニアから、
「job は engineer, teacher, fireman, dancer, policeman, actor のどれかしか返ってこないよ」
と言われました。

こういうときは enum を定義したくなりますね。
String 型だとどんな文字列でも入る余地がありますが、enum にすることでより範囲を狭めることができ、さまざまな場面で便利になります(Switch での分岐など)。コードから仕様を察することができますし、予期せぬエラーに気付きやすいです。

dart で enum を定義するときは次のように書きます。

enum Job {
  engineer,
  teacher,
  fireman,
  dancer,
  policeman,
  actor,
}

では JSON で受け取った文字列を enum に変換してみましょう。

あなたならどう書きますか?

switch 文が使えそうだと思われるかもしれません。
その場合、およそこのようになるでしょう。

Job string2Job(String value) {
  switch (value) {
    case 'engineer':
      return Job.engineer;
    case 'teacher':
      return Job.teacher;
    case 'fireman':
      return Job.fireman;
    case 'dancer':
      return Job.dancer;
    case 'policeman':
      return Job.policeman;
    case 'actor':
      return Job.actor;
    default:
      throw Exception('No enum value');
  }
}

しかしもっと簡単に書く方法があります。

Job.values.byName(value);

です。

この機能は dart 2.15 から使用可能になりました。

ではこれを使って JSON データから User インスタンスを作成する fromJson メソッドをつくってみましょう。

factory User.fromJson(String json) {
  final map = jsonDecode(json) as Map<String, dynamic>;
  return User(
    name: map['name'],
    job: Job.values.byName(map['job']), // byNameを使えば簡単にenumに変換できる。
  );
}

わざわざ自前で string2Job のような関数を定義しなくてもよいので便利です。

このように enum は少しずつ使いやすくなっています。
ですがまだまだ機能が豊富とは言えません。

またどこかの機会で extension をつかった拡張方法を紹介したいと思いますのでお楽しみに!

コピペで動くサンプルコード

import 'dart:convert';
import 'dart:developer';

import 'package:flutter/material.dart';

void main() {
  final user = User.fromJson(source);
  log(user.toString());
}

enum Job {
  engineer,
  teacher,
  fireman,
  dancer,
  policeman,
  actor,
}

const source = '''
{
  "name": "こんぶ",
  "job": "engineer"
}
''';

class User {
  User({
    required this.name,
    required this.job,
  });

  factory User.fromJson(String json) {
    final map = jsonDecode(json) as Map<String, dynamic>;
    return User(
      name: map['name'],
      job: Job.values.byName(map['job']),
    );
  }
  
  String toString() {
    return 'User : name=$name, job=${job.name}'; // .nameで元の文字列を取り出せる。
  }

  final String name;
  final Job job;
}