web-dev-qa-db-fra.com

Enum from String

J'ai un Enum et une fonction pour le créer à partir d'un String parce que je n'ai pas pu trouver un moyen intégré de le faire

enum Visibility{VISIBLE,COLLAPSED,HIDDEN}

Visibility visibilityFromString(String value){
  return Visibility.values.firstWhere((e)=>
      e.toString().split('.')[1].toUpperCase()==value.toUpperCase());
}

//used as
Visibility x = visibilityFromString('COLLAPSED');

mais il semble que je doive réécrire cette fonction pour chaque Enum que j'ai, y a-t-il un moyen d'écrire la même fonction là où elle prend le type Enum comme paramètre? J'ai essayé mais j'ai compris que je ne pouvais pas lancer de casting sur Enum.

//is something with the following signiture actually possible?
     dynamic enumFromString(Type enumType,String value){

     }
36
FPGA

En utilisant des miroirs, vous pourriez forcer un certain comportement. J'avais deux idées en tête. Malheureusement, Dart ne prend pas en charge les fonctions tapées:

import 'Dart:mirrors';

enum Visibility {VISIBLE, COLLAPSED, HIDDEN}

class EnumFromString<T> {
  T get(String value) {
    return (reflectType(T) as ClassMirror).getField(#values).reflectee.firstWhere((e)=>e.toString().split('.')[1].toUpperCase()==value.toUpperCase());
  }
}

dynamic enumFromString(String value, t) {
  return (reflectType(t) as ClassMirror).getField(#values).reflectee.firstWhere((e)=>e.toString().split('.')[1].toUpperCase()==value.toUpperCase());
}

void main() {
  var converter = new EnumFromString<Visibility>();

  Visibility x = converter.get('COLLAPSED');
  print(x);

  Visibility y = enumFromString('HIDDEN', Visibility);
  print(y);
}

Les sorties:

Visibility.COLLAPSED
Visibility.HIDDEN
10
Robert

Les miroirs ne sont pas toujours disponibles, mais heureusement, vous n'en avez pas besoin. C'est assez compact et devrait faire ce que vous voulez.

enum Fruit { Apple, banana }

// Convert to string
String str = Fruit.banana.toString();

// Convert to enum
Fruit f = Fruit.values.firstWhere((e) => e.toString() == str);

assert(f == Fruit.banana);  // it worked

Fix: Comme mentionné par @frostymarvelous dans la section commentaires, c'est une implémentation correcte:

Fruit f = Fruit.values.firstWhere((e) => e.toString() == 'Fruit.' + str);
86
Collin Jackson

Ma solution est identique à la solution de Rob C mais sans interpolation de chaîne:

T getEnumFromString<T>(Iterable<T> values, String value) {
  return values.firstWhere((type) => type.toString().split(".").last == value,
      orElse: () => null);
}
9
Rambutan

Collin Jackson la solution n'a pas fonctionné pour moi car Dart stringifie les énumérations en EnumName.value plutôt que simplement value (par exemple, Fruit.Apple), et j'essayais de convertir la valeur de chaîne comme Apple plutôt que de convertir Fruit.Apple dès le départ.

Dans cet esprit, c'est ma solution pour le problème d'énumération de chaîne

enum Fruit {Apple, banana}

Fruit getFruitFromString(String fruit) {
  fruit = 'Fruit.$fruit';
  return Fruit.values.firstWhere((f)=> f.toString() == fruit, orElse: () => null);
}
7
Spencer

Tout cela est tellement compliqué que j'ai créé une bibliothèque simple qui fait le travail:

https://pub.dev/packages/enum_to_string

import 'package:enum_to_string:enum_to_string.Dart';

enum TestEnum { testValue1 };

convert(){
    String result = EnumToString.parse(TestEnum.testValue1);
    //result = 'testValue1'

    String resultCamelCase = EnumToString.parseCamelCase(TestEnum.testValue1);
    //result = 'Test Value 1'

    final result = EnumToString.fromString(TestEnum.values, "testValue1");
    // TestEnum.testValue1
}
6
Ryan Knell

J'ai amélioré Collin Jackson réponse en utilisant Dart 2.7 Méthodes d'extension pour le rendre plus élégant.

enum Fruit { Apple, banana }

extension EnumParser on String {
  Fruit toFruit() {
    return Fruit.values.firstWhere(
        (e) => e.toString().toLowerCase() == 'fruit.$this'.toLowerCase(),
        orElse: () => null); //return null if not found
  }
}

main() {
  Fruit Apple = 'Apple'.toFruit();
  assert(Apple == Fruit.Apple); //true
}
3
mbartn

Il existe quelques packages enums qui m'ont permis d'obtenir uniquement la chaîne enum plutôt que la chaîne type.value (Apple, pas Fruit.Apple).

https://pub.dartlang.org/packages/built_value (c'est plus à jour)

https://pub.dartlang.org/packages/enums

void main() {
  print(MyEnum.nr1.index);            // prints 0
  print(MyEnum.nr1.toString());       // prints nr1
  print(MyEnum.valueOf("nr1").index); // prints 0
  print(MyEnum.values[1].toString())  // prints nr2
  print(MyEnum.values.last.index)     // prints 2
  print(MyEnum.values.last.myValue);  // prints 15
}  
2
atreeon

@Collin Jackson a une très bonne réponse OMI. J'avais utilisé une boucle for-in pour obtenir un résultat similaire avant de trouver cette question. Je passe définitivement à l'utilisation de la méthode firstWhere.

En développant sa réponse, voici ce que j'ai fait pour supprimer le type des chaînes de valeur:

enum Fruit { Apple, banana }

class EnumUtil {
    static T fromStringEnum<T>(Iterable<T> values, String stringType) {
        return values.firstWhere(
                (f)=> "${f.toString().substring(f.toString().indexOf('.')+1)}".toString()
                    == stringType, orElse: () => null);
    }
}

main() {
    Fruit result = EnumUtil.fromStringEnum(Fruit.values, "Apple");
    assert(result == Fruit.Apple);
}

Peut-être que quelqu'un trouvera cela utile ...

1
Rob C

J'ai eu le même problème avec la construction d'objets à partir de JSON. En JSON, les valeurs sont des chaînes, mais je voulais que l'énumération valide si la valeur est correcte. J'ai écrit cet assistant qui fonctionne avec n'importe quelle énumération, pas une spécifiée:

class _EnumHelper {


 var cache = {};

  dynamic str2enum(e, s) {
    var o = {};
    if (!cache.containsKey(e)){
      for (dynamic i in e) {
        o[i.toString().split(".").last] = i;
      }
      cache[e] = o;
    } else {
      o = cache[e];
    }
    return o[s];
  }
}

_EnumHelper enumHelper = _EnumHelper();

Usage:

enumHelper.str2enum(Category.values, json['category']);

PS. Je n'ai pas utilisé de types exprès ici. enum n'est pas du type dans Dart et le traiter comme on complique les choses. La classe est utilisée uniquement à des fins de mise en cache.

1
Adrian Kalbarczyk

J'ai eu le même problème dans l'un de mes projets et les solutions existantes n'étaient pas très propres et ne prenaient pas en charge les fonctionnalités avancées telles que la sérialisation/désérialisation json.

Flutter ne prend actuellement pas en charge enum enum avec des valeurs, cependant, j'ai réussi à développer un package d'assistance Vnum en utilisant l'implémentation de classe et de réflecteurs pour surmonter ce problème.

Adresse du référentiel:

https://github.com/AmirKamali/Flutter_Vnum

Pour répondre à votre problème en utilisant Vnum, vous pouvez implémenter votre code comme ci-dessous:

@VnumDefinition
class Visibility extends Vnum<String> {
  static const VISIBLE = const Visibility.define("VISIBLE");
  static const COLLAPSED = const Visibility.define("COLLAPSED");
  static const HIDDEN = const Visibility.define("HIDDEN");

  const Visibility.define(String fromValue) : super.define(fromValue);
  factory Visibility(String value) => Vnum.fromValue(value,Visibility);
}

Vous pouvez l'utiliser comme:

var visibility = Visibility('COLLAPSED');
print(visibility.value);

Il y a plus de documentation dans le dépôt github, j'espère que cela vous aidera.

1
Amir.n3t

Voici la fonction qui convertit une chaîne donnée en type énumération:

EnumType enumTypeFromString(String typeString) => EnumType.values
    .firstWhere((type) => type.toString() == "EnumType." + typeString);

Et voici comment convertir un type d'énumération donné en chaîne:

String enumTypeToString(EnumType type) => type.toString().split(".")[1];
1
Ugurcan Yildirim

Voici une manière alternative à l'approche de @ mbartn en utilisant des extensions, en étendant le enum lui-même au lieu de String.

Plus rapide, mais plus fastidieux

// We're adding a 'from' entry just to avoid having to use Fruit.Apple['banana'],
// which looks confusing.
enum Fruit { from, Apple, banana }

extension FruitIndex on Fruit {
  // Overload the [] getter to get the name of the fruit.
  operator[](String key) => (name){
    switch(name) {
      case 'banana': return Fruit.banana;
      case 'Apple':  return Fruit.Apple;
      default:       throw RangeError("enum Fruit contains no value '$name'");
    }
  }(key);
}

void main() {
  Fruit f = Fruit.from["banana"];
  print("$f is ${f.runtimeType}"); // Outputs: Fruit.banana is Fruit
}

Moins de tedius, mais plus lent

Si les performances de O(n) sont acceptables, vous pouvez également incorporer la réponse de @Collin Jackson:

// We're adding a 'from' entry just to avoid having to use Fruit.Apple['banana']
// which looks confusing.
enum Fruit { from, Apple, banana }

extension FruitIndex on Fruit {
  // Overload the [] getter to get the name of the fruit.
  operator[](String key) =>
    Fruit.values.firstWhere((e) => e.toString() == 'Fruit.' + key);
}

void main() {
  Fruit f = Fruit.from["banana"];
  print("$f is ${f.runtimeType}"); // Outputs: Fruit.banana is Fruit
}
1
Krista

Je pense que mon approche est légèrement différente, mais pourrait être plus pratique dans certains cas. Enfin, nous avons parse et tryParse pour les types d'énumération:

import 'Dart:mirrors';

class Enum {
  static T parse<T>(String value) {
    final T result = (reflectType(T) as ClassMirror).getField(#values)
        .reflectee.firstWhere((v)=>v.toString().split('.').last.toLowerCase() == value.toLowerCase()) as T;
    return result;
  }

  static T tryParse<T>(String value, { T defaultValue }) {
    T result = defaultValue;
    try {
      result = parse<T>(value);
    } catch(e){
      print(e);
    }
    return result;
  }
}

EDIT: cette approche ne fonctionne PAS dans les applications Flutter, par défaut les miroirs sont bloqués dans Flutter car cela fait que les packages générés sont très volumineux.

0
NET3