通用模板字符串,如 Dart 中的 Python

Generic Template String like in Python in Dart

在python中,我经常使用字符串作为模板,例如

templateUrl = '{host}/api/v3/{container}/{resourceid}'  
params = {'host': 'www.api.com', 'container': 'books', 'resourceid': 10}  
api.get(templateUrl.format(**params))  

这允许简单的基础 class 设置等。我怎样才能在飞镖中做同样的事情?

我假设我需要创建一个实用函数来解析模板并手动替换,但真的希望有现成的东西可以使用。

也许是一个带有 format 方法的 TemplateString class,该方法采用 Map 对 name/value 来替换字符串。

注意:objective 具有通用的“格式”或“插值”功能,不需要事先知道模板中将存在哪些标签或名称。

进一步说明:模板本身在设置时并未解析。具体来说,模板在代码中的一个地方定义,然后在其他许多地方使用。

在 Dart 中更容易。示例代码如下:

String host = "www.api.com"
String container = "books"
int resourceId = 10

String templateUrl = "$host/api/v3/$container/${resourceId.toString()}"

有了地图,您可以进行如下操作:

Map<String, String> params = {'host': 'www.api.com', 'container': 'books', 'resourceid': 10}  

String templateUrl = "${params['host']}/api/v3/${params['container']}/${params['resourceId']}"

注意:以上代码将Map定义为<String, String>。您可能需要 <String, Dynamic>(并使用 .toString()

Dart 没有允许您在 运行时.
将值插入模板的通用模板字符串功能 Dart 只允许您使用 $ 语法在字符串中插入带有变量的字符串,例如var string = '$domain/api/v3/${actions.get}'。您需要事先在代码中定义所有变量。

但是,您可以轻松创建自己的实现。

实施

您在自己的问题中几乎已经解释了如何做到这一点:您传递一个映射并使用它来使用 [] 运算符对参数进行通用访问。

为了将模板字符串转换成易于访问的内容,我会简单地创建另一个包含固定组件的 List,例如 /api/v3/ 和另一个包含通用组件的 Map他们的名字和他们在模板字符串中的位置。

class TemplateString {
  final List<String> fixedComponents;
  final Map<int, String> genericComponents;

  int totalComponents;

  TemplateString(String template)
      : fixedComponents = <String>[],
        genericComponents = <int, String>{},
        totalComponents = 0 {
    final List<String> components = template.split('{');

    for (String component in components) {
      if (component == '') continue; // If the template starts with "{", skip the first element.

      final split = component.split('}');

      if (split.length != 1) {
        // The condition allows for template strings without parameters.
        genericComponents[totalComponents] = split.first;
        totalComponents++;
      }

      if (split.last != '') {
        fixedComponents.add(split.last);
        totalComponents++;
      }
    }
  }

  String format(Map<String, dynamic> params) {
    String result = '';

    int fixedComponent = 0;
    for (int i = 0; i < totalComponents; i++) {
      if (genericComponents.containsKey(i)) {
        result += '${params[genericComponents[i]]}';
        continue;
      }
      result += fixedComponents[fixedComponent++];
    }

    return result;
  }
}

下面是一个使用示例,希望结果如您所愿:

main() {
  final templateUrl = TemplateString('{host}/api/v3/{container}/{resourceid}');
  final params = <String, dynamic>{'host': 'www.api.com', 'container': 'books', 'resourceid': 10};

  print(templateUrl.format(params)); // www.api.com/api/v3/books/10
}

这里是as a Gist

这是我的解决方案:

extension StringFormating on String {
  String format(List<String> values) {
    int index = 0;
    return replaceAllMapped(new RegExp(r'{.*?}'), (_) {
      final value = values[index];
      index++;
      return value;
    });
  }

  String formatWithMap(Map<String, String> mappedValues) {
    return replaceAllMapped(new RegExp(r'{(.*?)}'), (match) {
      final mapped = mappedValues[match[1]];
      if (mapped == null)
        throw ArgumentError(
            '$mappedValues does not contain the key "${match[1]}"');
      return mapped;
    });
  }
}

这为您提供了与 python 提供的非常相似的功能:

"Test {} with {}!".format(["it", "foo"]);
"Test {a} with {b}!".formatWithMap({"a": "it", "b": "foo"})

两者都 return“用 foo 测试它!”