Interpolación de Strings en Groovy y Map con valores por Default

Todos los usuarios de Groovy o Grails saben lo bueno que es la interpolación de GStrings, la cual funciona tan bien que a veces nos olvidamos que no son Strings de java lo que estamos manejando.

Hoy me surgió el problema de leer un string desde un archivo y en ese string tener expresiones como ${myvar} las cuales debían ser resueltas utilizando un mapa con las claves y valores necesarios.

El primer problema era como procesar ese string como si fuera un GString, buscando en Internet hay varias formas de hacerlo, la más simple que encontré o se me ocurrió es utilizar el llamado SimpleTemplateEngine de Groovy.

import groovy.text.SimpleTemplateEngine

def stringTemplate = 'Hola ${nombre} ${apellido}'
def variables = [nombre: 'Fernando', apellido: 'Niwes']

def simpleTemplateEngine = new SimpleTemplateEngine()
def template = simpleTemplateEngine.createTemplate(stringTemplate)
def result = template.make(variables)

println result

Aunque en muchos casos esto es más que suficiente, yo tenia un requisito más: Cuando interpolaba el String podían faltarme algunas variables por ejemplo, que en el caso anterior el Hash no hubiera tenido el atributo “apellido”. En esa circunstancia quería que quedara como ${apellido}

El código tal cual está generaría una excepción. Específicamente un MissingPropertyException lo cual era inaceptable.

Además, por las peculiaridades del proyecto, yo no tenía idea de que variables se usarían en el String, por lo que no podía chequear o setearle un valor prefijado a los datos del Hash antes de invocar el método Make.

Hablando con mis compañeros se nos ocurrió resolver el problema usando propertyMissing pero incluso intentando de diferentes formas no logré encontrar la forma de que funcionara.

Luego, milagrosamente encontré una clase en el código fuente de Groovy llamada MapWithDefault que su descripción indica que es un Wrapper que permite a un mapa, retornar un valor por default cuando un dato no existe.

O sea, que había encontrado justo lo que necesitaba. Un poco más de información y un ejemplo de uso se puede ver en el sitio http://mrhaki.blogspot.com.ar/2010/07/groovy-goodness-map-with-default-values.html

Aplicando este nuevo conocimiento y el método de los Maps de groovy withDefault podemos lograr nuestro propósito de la siguiente manera.

import groovy.text.SimpleTemplateEngine

def stringTemplate = 'Hola ${nombre} ${apellido}'
def variables = [nombre: 'Fernando'].withDefault { name -> "\${${name}}" }

def simpleTemplateEngine = new SimpleTemplateEngine()
def template = simpleTemplateEngine.createTemplate(stringTemplate)
def result = template.make(variables)

println result

Noten en este ejemplo como, en el mapa he borrado el atributo apellido y además, llamo al método withDefault el cual si no encuentra un valor en el mapa, retorna el mismo nombre que estaba buscando, decorado con ${}

Espero me haya podido explicar correctamente, y que a alguien le sea de utilidad. En verdad fue un hallazgo que me facilitó mucho el trabajo.


Discussion Area - Leave a Comment