简体   繁体   English

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

[英]Generic Template String like in Python in Dart

In python, I often use strings as templates, eg在python中,我经常使用字符串作为模板,例如

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

This allows for easy base class setup and the like.这允许简单的基类设置等。 How can I do the same in dart?我怎样才能在飞镖中做同样的事情?

I'm assuming I will need to create a utility function to parse the template and substitute manually but really hoping there is something ready to use.我假设我需要创建一个实用函数来解析模板并手动替换,但真的希望有一些东西可以使用。

Perhaps a TemplateString class with a format method that takes a Map of name/value pairs to substitute into the string.可能是具有format方法的 TemplateString 类,该方法采用名称/值对的Map来替换字符串。

Note: the objective is to have a generic "format" or "interpolation" function that doesn't need to know in advance what tags or names will exist in the template.注意:目标是有一个通用的“格式”或“插值”功能,不需要提前知道模板中将存在哪些标签或名称。

Further clarification: the templates themselves are not resolved when they are set up.进一步说明:模板本身在设置时并未解析。 Specifically, the template is defined in one place in the code and then used in many other places.具体来说,模板在代码中的一处定义,然后在许多其他地方使用。

Dart does not have a generic template string functionality that would allow you to insert values into your template at runtime . Dart 没有通用的模板字符串功能,它允许您在运行时将值插入到模板中。
Dart only allows you to interpolate strings with variables using the $ syntax in strings, eg var string = '$domain/api/v3/${actions.get}' . Dart 只允许您使用字符串中的$语法插入带有变量的字符串,例如var string = '$domain/api/v3/${actions.get}' You would need to have all the variables defined in your code beforehand.您需要事先在代码中定义所有变量。

However, you can easily create your own implementation.但是,您可以轻松创建自己的实现。

Implementation执行

You pretty much explained how to do it in your question yourself: you pass a map and use it to have generic access to the parameters using the [] operator.您自己在问题中解释了如何做到这一点:您传递一个映射并使用它来使用[]运算符对参数进行通用访问。

To convert the template string into something that is easy to access, I would simply create another List containing fixed components, like /api/v3/ and another Map that holds generic components with their name and their position in the template string.要将模板字符串转换为易于访问的内容,我只需创建另一个包含固定组件的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;
  }
}

Here would be an example usage, I hope that the result is what you expected:这是一个示例用法,我希望结果是您所期望的:

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
}

Here it is as a Gist .这是一个要点

Here is my solution:这是我的解决方案:

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;
    });
  }
}

This gives you a very similar functionality to what python offers:这为您提供了与 python 提供的功能非常相似的功能:

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

both return "Test it with foo!"都返回“用 foo 测试它!”

It's even more easy in Dart.在 Dart 中更容易。 Sample code below :下面的示例代码:

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

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

With the map, you can do as follows :使用地图,您可以执行以下操作:

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

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

Note : The above code defines Map as <String, String> .注意:上面的代码将 Map 定义为<String, String> You might want <String, Dynamic> (and use .toString() )您可能需要<String, Dynamic> (并使用.toString()

Wouldn't it be simplest to just make it a function with named arguments?让它成为一个带有命名参数的函数不是最简单的吗? You could add some input validation if you wanted to.如果您愿意,可以添加一些输入验证。

String templateUrl({String host = "", String container = "", int resourceid = 0 }) {
    return "$host/api/v3/$container/$resourceId";
}

void main() {
    api.get(templateUrl(host:"www.api.com", container:"books", resourceid:10));    
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM