2011-10-12 15 views
5

Tengo un código que lee un archivo XML. Algunos de los atributos de los elementos que necesito procesar son opcionales. Estoy tratando de usar la Opción [T] para administrarlos. He escrito el siguiente chulo del tipo NodeSeq devuelto por el operador \ Nodo:Procesamiento de atributos xml opcionales en Scala

class NodeSeqWrapper(nodeSeq: NodeSeq) { 
    def textOption: Option[String] = { 
    val text = nodeSeq.text 
    if (text == null || text.length == 0) None else Some(text) 
    } 
} 
implicit def nodeSeqWrapper(nodeSeq: NodeSeq): NodeSeqWrapper = 
    new NodeSeqWrapper(nodeSeq) 

y luego lo llaman así:

(node \ "@attr").textOption.getOrElse("Some default value") 

Si el nodo tiene el atributo "attr", este código obtiene valor Si no lo hace, se devuelve el valor "Algunos valores predeterminados".

¿Cómo puedo mejorar esto? ¿Hay alguna forma de plegar la definición de clase en el método implícito? ¿Hay una mejor manera de obtener valores de atributo "opcionales"? ¿Estoy usando Option[T] "correctamente"?

+0

Es posible que desee llamar a su método 'textOption', en el mismo estilo que los métodos' head' de 'Seq' y' head'. –

+0

Suena bien. Cambiaré mi código. Actualizando la pregunta también. – Ralph

Respuesta

4

Diría que lo estás haciendo de una manera muy idiomática, sí.

Usted puede "doblar las definiciones" de la siguiente manera:

implicit def enrichNodeSeq(nodeSeq: NodeSeq) = new AnyRef { 
    def textOption : Option[String] = { 
    val text = nodeSeq.text 
    if (text == null || text.length == 0) None else Some(text) 
    } 
} 

Si siempre está solicitando .getOrElse(...) en el resultado, también lo desea, puede definir una segunda versión textOrElse(elze : String) : String:

implicit def enrichNodeSeq(nodeSeq: NodeSeq) = new AnyRef { 
    def textOption : Option[String] = { 
    val text = nodeSeq.text 
    if (text == null || text.length == 0) None else Some(text) 
    } 

    def textOrElse(elze : String) : String = textOption.getOrElse(elze) 
} 

Eso hará las cosas un poco más concisas.

scala> (<b>Hello</b> : NodeSeq).textOrElse("No text found.") 
resN: String = Hello 
scala> (<br /> : NodeSeq).textOrElse("No text found.") 
resM: String = No text found. 
+0

Eso es lo que estaba buscando. Intenté usar una clase anónima en mi primer intento ('new {...}') y no funcionó. También me gusta el método 'textOrElse'. Gracias. – Ralph

+2

Tenga en cuenta que "plegar las definiciones" hace que la reflexión de Java se use cada vez que llame a sus métodos adicionales. Declarar la clase extra no lo hace. –

+0

@ Jean-PhilippePellet ¡Buen punto! Sospecho que el compilador JVM JIT es bastante bueno en el manejo de llamadas reflexivas donde el nombre del método se da como una cadena estática, sin embargo. – Philippe

2

La respuesta se puede mejorar desde Scala 2.10 con la introducción de clases implícitas.

http://docs.scala-lang.org/overviews/core/implicit-classes.html

El ejemplo del op dio puede ser re-escrita usando una clase implícita de este modo:

object SomeExtensions { 

    implicit class ExtendedNodeSeq(nodeSeq: NodeSeq) { 
    def textOption: Option[String] = { 
     val text = nodeSeq.text 
     if (text == null || text.length == 0) None else Some(text) 
    } 
    } 

} 

Tenga en cuenta que el ejemplo sigue a un par de las restricciones para las clases de casos:

  1. Deben definirse dentro de otro rasgo/clase/objeto.
  2. Solo pueden tomar un argumento no implícito en su constructor.
Cuestiones relacionadas