EXPERIMENTAL / DRAFT


While experimenting Play21/Json Zipper in my previous article, I needed to match patterns on JsPath and decided to explore a bit this topic.

This article just presents my experimentations on JsPath pattern matching so that people interested in the topic can tell me if they like it or not and what they would add or remove. So don’t hesitate to let comments about it.

If the result is satisfying, I’ll propose it to Play team ;)

Let’s go to samples as usual.

Very simple pattern matching

match/scale-style

1
2
3
4
5
scala> __ \ "toto" match {
  case __ \ key => Some(key)
  case _ => None
}
res0: Option[String] = Some(toto)

val-style

1
2
scala> val _ \ toto = __ \ "toto"
toto: String = toto

Note that I don’t write val __ \ toto = __ \ "toto" (2x Underscore) as you would expect.

Why? Let’s write it:

1
2
3
scala> val __ \ toto = __ \ "toto"
<console>:20: error: recursive value x$1 needs type
val __ \ toto = __ \ "toto"

Actually, 1st __ is considered as a variable to be affected by Scala compiler. Then the variable __ appears on left and right side which is not good.

So I use _ to ignore its value because I know it’s __. If I absolutely wanted to match with __, you would have written:

1
2
scala> val JsPath \ toto = __ \ "toto"
toto: String = toto

Pattern matching with indexed path

1
2
3
4
5
6
7
8
9
scala> val (_ \ toto)@@idx = (__ \ "toto")(2)
toto: String = toto
idx: Int = 2

scala> (__ \ "toto")(2) match {
  case (__ \ "toto")@@idx => Some(idx)
  case _      => None
}
res1: Option[Int] = Some(2)

Note the usage of @@ operator that you can dislike. I didn’t find anything better for now but if anyone has a better idea, please give it to me ;)


Pattern matching the last element of a JsPath

1
2
scala> val _ \ last = __ \ "alpha" \ "beta" \ "delta" \ "gamma"
last: String = gamma

Using _, I ignore everything before gamma node.


Matching only the first element and the last one

1
2
3
4
5
6
7
8
scala> val _ \ first \?\ last = __ \ "alpha" \ "beta" \ "gamma" \ "delta"
first: String = alpha
last: String = delta

scala> val (_ \ first)@@idx \?\ last = (__ \ "alpha")(2) \ "beta" \ "gamma" \ "delta"
first: String = alpha
idx: Int = 2
last: String = delta

Note the \?\ operator which is also a temporary choice: I didn’t want to choose \\ ause \?\ operator only works in the case where you match between the first and the last element of the path and not between anything and anything…


A few more complex cases

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
scala> val (_ \ alpha)@@idx \ beta \ gamma \ delta = (__ \ "alpha")(2) \ "beta" \ "gamma" \ "delta"
alpha: String = alpha
idx: Int = 2
beta: String = beta
gamma: String = gamma
delta: String = delta

scala> val (_ \ alpha)@@idx \ _ \ _ \ delta = (__ \ "alpha")(2) \ "beta" \ "gamma" \ "delta"
alpha: String = alpha
idx: Int = 2
delta: String = delta

scala> val _@@idx \?\ gamma \ delta = (__ \ "alpha")(2) \ "beta" \ "gamma" \ "delta"
idx: Int = 2
gamma: String = gamma
delta: String = delta

scala> (__ \ "alpha")(2) \ "beta" \ "gamma" \ "delta" match {
  case _@@2 \ "beta" \ "gamma" \ _ => true
  case _ => false
}
res4: Boolean = true

And finally using regex?

1
2
3
4
5
6
7
8
scala> val pattern = """al(\d)*pha""".r
pattern: scala.util.matching.Regex = al(\d)*pha

scala> (__ \ "foo")(2) \ "al1234pha" \ "bar" match {
  case (__ \ "foo")@@idx \ pattern(_) \ "bar" => true
  case _ => false
}
res6: Boolean = true

So, I think we can provide more features and now I’m going to use it with my JsZipper stuff in my next article ;)

If you like it, tell it!

Have fun!