【问题标题】:String replacement in java, similar to a velocity templatejava中的字符串替换,类似于速度模板
【发布时间】:2011-04-08 23:57:50
【问题描述】:

Java 中是否有任何String 替换机制,我可以在其中传递带有文本的对象,并在字符串出现时替换它。
例如,文本是:

Hello ${user.name},
    Welcome to ${site.name}. 

我拥有的对象是"user""site"。我想用对象中的等效值替换${} 中给出的字符串。这与我们替换速度模板中的对象相同。

【问题讨论】:

  • 替换在哪里?一类?一个JSP?字符串有一个格式化方法,如果你只是:String.format("Hello %s", username);
  • @Droo:在示例中,字符串类似于Hello ${user.name},而不是Hello %sHello {0}
  • 如果你需要看起来像velocity,闻起来像velocity的东西,也许是velocity? :)
  • @Droo:它不是一个类。我在“字符串”变量中有上述文本,并希望将 ${} 中所有出现的字符串替换为相应对象中的值。例如,将所有 ${user.name} 替换为“user”对象中的 name 属性。
  • @serg:是的,它是一个速度代码。我想从我的代码中删除速度。

标签: java string reflection velocity


【解决方案1】:

使用来自 Apache Commons Text 的 StringSubstitutor

https://commons.apache.org/proper/commons-text/

它会为你做这件事(以及它的开源......)

 Map<String, String> valuesMap = new HashMap<String, String>();
 valuesMap.put("animal", "quick brown fox");
 valuesMap.put("target", "lazy dog");
 String templateString = "The ${animal} jumped over the ${target}.";
 StringSubstitutor sub = new StringSubstitutor(valuesMap);
 String resolvedString = sub.replace(templateString);

【讨论】:

【解决方案2】:

看看java.text.MessageFormat 类,MessageFormat 获取一组对象,对其进行格式化,然后将格式化的字符串插入到模式中适当的位置。

Object[] params = new Object[]{"hello", "!"};
String msg = MessageFormat.format("{0} world {1}", params);

【讨论】:

  • 谢谢!我知道 java 应该有一个内置的方法来做到这一点,而不必使用可怕的模板引擎来做这么简单的事情!
  • 似乎 String.format 可以做任何事情——stackoverflow.com/questions/2809633/…
  • +1。请注意,format 也采用 Object... 可变参数,因此您可以在首选 format("{0} world {1}", "Hello", "!"); 的地方使用这种更简洁的语法
  • 应该注意MessageFormat 只能可靠地用于其同名,显示消息,而不是用于技术格式重要的输出。例如,数字将根据区域设置进行格式化,使它们对技术用途无效。
【解决方案3】:

我的首选方式是 String.format(),因为它是单行且不需要第三方库:

String message = String.format("Hello! My name is %s, I'm %s.", name, age); 

我经常使用这个,例如在异常消息中,例如:

throw new Exception(String.format("Unable to login with email: %s", email));

提示:您可以输入任意数量的变量,因为format() 使用Varargs

【讨论】:

  • 当您需要多次重复相同的参数时,这不太有用。例如:String.format("Hello! My name is %s, I'm %s. Why is my name %s you ask? Well I'm only %s years old so I don't know", name, age, name, age);。此处的其他答案只需要指定每个参数一次。
  • @asherbar 您可以在格式字符串中使用参数索引说明符,例如String.format("Hello! My name is %1$s, I'm %2$s. Why is my name %1$s you ask? Well I'm only %2$s years old so I don't know", name, age)
  • @jazzpi 我从来不知道。谢谢!
【解决方案4】:

我整理了一个小测试实现。基本思想是调用format 并传入格式字符串、对象映射以及它们在本地拥有的名称。

下面的输出是:

我的狗名叫 fido,Jane Doe 拥有它。

public class StringFormatter {

    private static final String fieldStart = "\\$\\{";
    private static final String fieldEnd = "\\}";

    private static final String regex = fieldStart + "([^}]+)" + fieldEnd;
    private static final Pattern pattern = Pattern.compile(regex);

    public static String format(String format, Map<String, Object> objects) {
        Matcher m = pattern.matcher(format);
        String result = format;
        while (m.find()) {
            String[] found = m.group(1).split("\\.");
            Object o = objects.get(found[0]);
            Field f = o.getClass().getField(found[1]);
            String newVal = f.get(o).toString();
            result = result.replaceFirst(regex, newVal);
        }
        return result;
    }

    static class Dog {
        public String name;
        public String owner;
        public String gender;
    }

    public static void main(String[] args) {
        Dog d = new Dog();
        d.name = "fido";
        d.owner = "Jane Doe";
        d.gender = "him";
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("d", d);
        System.out.println(
           StringFormatter.format(
                "My dog is named ${d.name}, and ${d.owner} owns ${d.gender}.", 
                map));
    }
}

注意:由于未处理的异常,这不会编译。但它使代码更容易阅读。

另外,我不喜欢您必须自己在代码中构建映射,但我不知道如何以编程方式获取局部变量的名称。最好的方法是记住在创建对象后立即将其放入地图中。

以下示例会从示例中生成您想要的结果:

public static void main(String[] args) {
    Map<String, Object> map = new HashMap<String, Object>();
    Site site = new Site();
    map.put("site", site);
    site.name = "StackOverflow.com";
    User user = new User();
    map.put("user", user);
    user.name = "jjnguy";
    System.out.println(
         format("Hello ${user.name},\n\tWelcome to ${site.name}. ", map));
}

我还要提一下,我不知道 Velocity 是什么,所以我希望这个答案是相关的。

【讨论】:

  • 这就是我要找的。感谢您提供实施。我正在尝试并得到不正确的结果。 :D。无论如何它解决了我的问题。
  • @Joe,很高兴我能帮上忙。这是我最终练习编写一些使用 Java 反射的代码的好借口。
  • 不错的实用程序。从 java 9 开始,您现在可以内联地图并将填充的地图作为参数传递,例如Map.of("user", user, "site", site),而不是单独创建地图。这将进一步简化调用“格式”方法。
【解决方案5】:

这里概述了如何执行此操作。将其实现为实际代码应该相对简单。

  1. 创建将在模板中引用的所有对象的映射。
  2. 使用正则表达式在模板中查找变量引用并将其替换为它们的值(参见步骤 3)。 Matcher 类可用于查找和替换。
  3. 在点处拆分变量名。 user.name 将变为 username。在您的地图中查找user 以获取对象并使用reflection 从对象中获取name 的值。假设您的对象具有标准 getter,您将寻找方法 getName 并调用它。

【讨论】:

  • 呵呵,刚看到这个答案。它与我的相同。请让我知道您对我的实施的看法。
【解决方案6】:

有几个表达式语言实现可以为您执行此操作,可能比使用您自己的实现更好,或者如果您的需求增长,请参阅例如JUELMVEL

我喜欢并已在至少一个项目中成功使用过 MVEL。

另请参阅 Stackflow 帖子 JSTL/JSP EL (Expression Language) in a non JSP (standalone) context

【讨论】:

  • 所有链接都失效了
  • 嗯,答案是 10 多年前给出的,我刚刚更新了唯一的死链接... :-)
【解决方案7】:

我创建了这个使用 vanilla Java 的实用程序。它将 String.format 中的两种格式...{}%s 样式结合到一个方法调用中。请注意,它只替换空的{} 括号,而不是{someWord}

public class LogUtils {

    public static String populate(String log, Object... objects) {
        log = log.replaceAll("\\{\\}", "%s");
        return String.format(log, objects);
    }

    public static void main(String[] args) {
        System.out.println(populate("x = %s, y ={}", 5, 4));;
    }

}

【讨论】:

    【解决方案8】:

    Handlebars.java 在类似 Velocity 的语法和其他服务器端模板功能方面可能是更好的选择。

    http://jknack.github.io/handlebars.java/

    Handlebars handlebars = new Handlebars();
    Template template = handlebars.compileInline("Hello {{this}}!");
    System.out.println(template.apply("Handlebars.java"));
    

    【讨论】:

      【解决方案9】:

      没有什么开箱即用的东西可以与速度相媲美,因为速度就是为了解决这个问题而编写的。您可以尝试的最接近的方法是查看格式化程序

      http://cupi2.uniandes.edu.co/site/images/recursos/javadoc/j2se/1.5.0/docs/api/java/util/Formatter.html

      然而,据我所知,格式化程序是为了在 Java 中提供类似 C 的格式化选项而创建的,因此它可能不会完全满足您的要求,但欢迎您尝试 :)。

      【讨论】:

        【解决方案10】:

        我在 java 中使用 GroovyShell 来解析带有 Groovy GString 的模板:

        Binding binding = new Binding();
        GroovyShell gs = new GroovyShell(binding);
        // this JSONObject can also be replaced by any Java Object
        JSONObject obj = new JSONObject();
        obj.put("key", "value");
        binding.setProperty("obj", obj)
        String str = "${obj.key}";
        String exp = String.format("\"%s\".toString()", str);
        String res = (String) gs.evaluate(exp);
        // value
        System.out.println(str);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-11
          • 1970-01-01
          • 2019-04-14
          • 2011-04-03
          • 1970-01-01
          相关资源
          最近更新 更多