2011-03-08 5 views
6

Relacionado con Adding an XML attribute depending on an Option, me gustaría agregar una etiqueta XML en Scala dependiendo de una opción.¿Cómo agrego una etiqueta XML o no, dependiendo de una opción en Scala?

scala> def toXml(value1: String, value2: Option[String]) = 
    | <body> 
    | <tag1>{value1}</tag1> 
    | {value2 map (x => <tag2>{x}</tag2>) flatten} 
    | </body> 
toXml: (value1: String,value2: Option[String])scala.xml.Elem 

Si existe la opción:

scala> toXml("value1", Some("value2")) 
res1: scala.xml.Elem = 
<body> 
    <tag1>value1</tag1> 
    <tag2>value2</tag2> 
</body> 

Si la opción no existe:

scala> toXml("value1", None) 
res2: scala.xml.Elem = 
<body> 
    <tag1>value1</tag1> 

</body> 

Quiero generar una gran cantidad de etiquetas en función de una opción, me preguntaba si una solución más concisa podría ser encontrada. Por ejemplo, proxenetismo la clase Elem con el? operador y utilizar de esta manera (? Es una solución sucia ya que el valor2 opción se convierte en una cadena antes de que el operador se llama):

scala> def toXml2(value1: String, value2: Option[String]) = 
    | <body> 
    | <tag1>{value1}</tag1> 
    | {<tag2>{value2}</tag2>?} 
    | </body> 

¿Alguna idea?

Respuesta

11

Su ? función deseada se puede realizar de esta manera:

implicit def optionElem(e: Elem) = new { 
    def ? : NodeSeq = { 
    require(e.child.length == 1) 
    e.child.head match { 
     case atom: Atom[Option[_]] => atom.data match { 
     case None => NodeSeq.Empty 
     case Some(x) => e.copy(child = x match { 
      case n: NodeSeq => n 
      case x => new Atom(x) 
     }) 
     } 
     case _ => e 
    }  
    } 
} 

Para simplificar esto es sólo para los nodos childed individuales que muestran el concepto (supongo que debe ser capaz de escribir una función más general a partir de ahí en caso de que lo necesite)

Si la función implícita es en su alcance, el operador ? se puede utilizar igual que tú querías:

<body> 
{ <tag1>{ Some("Hello") }</tag1>? } 
{ <tag2>{ None }</tag2>? } 
{ <tag3>{ Some(<innerTag/>) }</tag3>? } 
{ <tag4><innerTag/></tag4>? } 
{ <tag5>{ None }</tag5>? } 
</body> 

se traducirá en:

<body> 
    <tag1>Hello</tag1> 

    <tag3><innerTag></innerTag></tag3> 
    <tag4><innerTag></innerTag></tag4> 

</body> 
6

por qué no utilizar una sentencia if como esta:

def toXml(value1:String,value2:Option[String]) = 
    <body> 
    <tag1>{value1}</tag1> 
    {if (value2 isDefined) <tag2>{value2.get}</tag2>} 
    </body> 

Que debe hacer el truco y es bastante comprensible, ¿verdad?

+1

Gracias Anne por la respuesta, esto es más claro que mi uso del mapa y aplanar. Sin embargo, me gustaría evitar copiar y pegar pruebas definidas para cada opción. Estoy buscando una solución donde el código se vea casi como el resultado XML. –

3

Puede también fold la opción:

{ value2.fold(NodeSeq.Empty)(x => <tag2>{x}</tag2>) } 
+0

Esto es mucho más simple que las otras respuestas. – Casey

2

Aquí es una versión simple que no utiliza la (discutible engañosa, ya que nada se acumula) Método de plegar:

value2.map(x => <tag2>{x}</tag>).getOrElse(NodeSeq.Empty) 

El resultado es el mismo que la respuesta de Zoltan.

Cuestiones relacionadas