Qwerky
Qwerky

Reputation: 18455

How do I format a string with properties from a bean

I want to create a String using a format, replacing some tokens in the format with properties from a bean. Is there a library that supports this or am I going to have to create my own implementation?

Let me demonstate with an example. Say I have a bean Person;

public class Person {
  private String id;
  private String name;
  private String age;

  //getters and setters
}

I want to be able to specify format strings something like;

"{name} is {age} years old."
"Person id {id} is called {name}."

and automatically populate the format placeholders with values from the bean, something like;

String format = "{name} is {age} old."
Person p = new Person(1, "Fred", "32 years");
String formatted = doFormat(format, person); //returns "Fred is 32 years old."

I've had a look at MessageFormat but this only seems to allow me to pass numeric indexes, not bean properties.

Upvotes: 7

Views: 3358

Answers (4)

Qwerky
Qwerky

Reputation: 18455

Rolled my own, testing now. Comments welcome.

import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BeanFormatter<E> {

  private Matcher matcher;
  private static final Pattern pattern = Pattern.compile("\\{(.+?)\\}");

  public BeanFormatter(String formatString) {
    this.matcher = pattern.matcher(formatString);
  }

  public String format(E bean) throws Exception {
    StringBuffer buffer = new StringBuffer();

    try {
      matcher.reset();
      while (matcher.find()) {
        String token = matcher.group(1);
        String value = getProperty(bean, token);
        matcher.appendReplacement(buffer, value);
      }
      matcher.appendTail(buffer);
    } catch (Exception ex) {
      throw new Exception("Error formatting bean " + bean.getClass() + " with format " + matcher.pattern().toString(), ex);
    }
    return buffer.toString();
  }

  private String getProperty(E bean, String token) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
    Field field = bean.getClass().getDeclaredField(token);
    field.setAccessible(true);
    return String.valueOf(field.get(bean));
  }

  public static void main(String[] args) throws Exception {
    String format = "{name} is {age} old.";
    Person p = new Person("Fred", "32 years", 1);

    BeanFormatter<Person> bf = new BeanFormatter<Person>(format);
    String s = bf.format(p);
    System.out.println(s);
  }

}

Upvotes: 4

hendry.fu
hendry.fu

Reputation: 309

Not quite close, but you can look at StringTemplate, your bean:

public static class User {
    public int id; // template can directly access via u.id
    private String name; // template can't access this
    public User(int id, String name) { this.id = id; this.name = name; }
    public boolean isManager() { return true; } // u.manager
    public boolean hasParkingSpot() { return true; } // u.parkingSpot
    public String getName() { return name; } // u.name
    public String toString() { return id+":"+name; } // u
}

Then you can render it like this:

ST st = new ST("<b>$u.id$</b>: $u.name$", '$', '$');
st.add("u", new User(999, "parrt"));
String result = st.render(); // "<b>999</b>: parrt"

Code sample above taken from ST4 Introduction

Upvotes: 0

Alonso Dominguez
Alonso Dominguez

Reputation: 7858

Don't really know how complex is the model you're up to consume but if you want to deal with object trees I would implement my own formatter using Jexl as expession language this way:

  1. Initialize a singleton Jexl engine
  2. Populate a MapContext with all the objects you want to consume when formatting strings
  3. Parse your strings and create a Jexl expression per "${}" construct you have.
  4. Evaluate the previous created expressions against the object context map.

The good thing about Jexl is that it will allow you to use method calls, not just properties.

Hope it helps.

Upvotes: 0

Boris Pavlović
Boris Pavlović

Reputation: 64640

Yes, it's possible using the Pojomatic library. Implement and plug in your own implementation of PojoFormatter. Pojomator#doToString(T) may be also interesting.

Upvotes: 1

Related Questions