Type annotation, loading from yaml

Hello! I am building an app which loads data from yaml, but I’m not sure how to turn a YamlList into a List<String>, not a List<dynamic>.

I would like to check if it’s all strings, else throw an exception. If that works, it should set the type to List<String>.

List myList = yamlData["title"].toList;
try {
  assert(myList.runtimeType is List<String>);
} on AssertionError {
  throw Exception('error msg');
}

I think this is fine, not sure if I need growable: false on the toList. I want to be able to pass myList into a function requiring a List<String> once I know that’s true.

What about converting the yanl to json first which can always be done?

Not sure it helps, but you can use whereType<String> to filter non-String values. If you want to make sure all elements are of type String, you can also first get the length of your list and compare with the length after the whereType<String> was called.

List myList = yamlData["title"].toList();
final stringList = myList.whereType<String>();

if (myList.length == stringList.length) {
  assert(myList.runtimeType is List<String>);
} else {
  throw Exception('error msg');
}

whereType<String> returns Iterable<String>, so do I have to use that to build a List<String> from it?

  List<dynamic> myList = yamlData["title"].toList();
  List<String> stringList = [];
  for (String item in myList.whereType<String>()) {
    stringList.add(item);
  }

This seems to work, I’m wondering if there is a more compact version of this?

And I’ve fixed my other yaml issues by realising I can do loadYaml() as Map to get it as a normal Map object rather than a YamlMap.

@escamoteur the only time I’m converting to json is when I’m saving what I’ve loaded to disk so far.

I have made this functionality a function as I realised I need to use it a couple times.

List<String> makeListDynamicIntoAListString({required List<dynamic> list, required String errorMessage}) {
  List<String> stringList = [];
  for (String item in list.whereType<String>()) {
    stringList.add(item);
  }
  if (list.length == stringList.length) {
    assert(stringList.runtimeType is List<String>);
  } else {
    throw Exception(errorMessage);
  }
  return stringList;
}

Few notes: stringList.runtimeType is List<String> is never true.

You could also write the code like this:

extension EnsureType on Iterable<dynamic> {
  List<T> ensureType<T>() {
    return [
      for (var (i, v) in this.indexed)
        if (v is T)
          v
        else
          throw Exception(
              "item at index $i is not a $T but ${v.runtimeType}: ${v}"),
    ];
  }
}

and then use it like this iterable.ensureType<String>()

1 Like

You can just do

yamlData["title"].whereType<String>().toList()

You might find reading the API docs at Iterable class - dart:core library - Dart API helpful.

I have to say working with YamlMap and JSON-like structures in general is not the most intuitive thing in Dart (and more generally, in strongly typed languages).

1 Like

How can you decide yaml to a map like what we have Witz jsondecode?