FintanH
FintanH

Reputation: 110

Extracting values from JSON Array of JSON Objects using Argonaut Lenses

It's my first time using argonauts and only have a small knowledge of lenses (enough to get by). I've spent a while trying to figure out the problem myself but getting nowhere.

I'm trying to build a lens to get a JsonArray (of Strings) from some JSON. I can get as far as the Object that has the Array but not sure what to do from there.

The JSON looks like:

json example

And my lens so far is this:

val hashtagsView = twitterEntitiesView >=> jsonObjectPL("hashtags") >=> jArrayPL

I'm not sure if that jArrayPL is correct either. What I would like to do is just retrieve the text from the Array.

So to wrap up, can anyone help me in finding out how to construct a lens that looks into hashtags and then for each element of the array look into the text, finally getting a values as a JsonArray.

Update:

With some help from Travis I have the following code compiling:

import argonaut._, Argonaut._
import monocle.std.list._, monocle.function.Each.each, monocle.function.Index.index
import scalaz._, Scalaz._

val \/-(json) = Parse.parse(rawJSON)
val lens = jObjectPrism
          .composeOptional(index("hashtags"))
          .composePrism(jArrayPrism)
          .composeTraversal(each[List[Json], Json])
          .composePrism(jObjectPrism)
          .composeOptional(index("text"))
          .composePrism(jStringPrism)

println(lens.getAll(json))

Unfortunately, I get a runtime error: scalaz.Scalaz$.ToEitherOps(Ljava/lang/Object;)Lscalaz/syntax/EitherOps; starting at the line val \/-(json) = Parse.parse(rawJSON)

Thanks in advance!

Upvotes: 2

Views: 1466

Answers (2)

Vasco
Vasco

Reputation: 1

Well, if you only want to extract the fields:

import argonaut._, Argonaut._
import scalaz._, Scalaz._

val doc = """{
  "hashtags": [
    { "indices": [0, 3], "text": "foo" },
    { "indices": [3, 6], "text": "bar" }
  ]
}"""

val \/-(json) = Parse.parse(doc)
val lense = jObjectPL
val hashtags = (lense >=> jsonObjectPL("hashtags") >=> jArrayPL).get(json)

hashtags.get.foreach(i => (lense >=> jsonObjectPL("indices") ).get(i).get.println )
hashtags.get.foreach(i => (lense >=> jsonObjectPL("text") ).get(i).get.println )

..or better

val ind = ((v:Json) =>(lense >=> jsonObjectPL("indices") ).get(v).get)
val text = ((v:Json) =>(lense >=> jsonObjectPL("text") ).get(v).get)

hashtags.get.foreach(i => (ind(i), text(i)).println )

Upvotes: 0

Travis Brown
Travis Brown

Reputation: 139058

Are you willing to use the Monocle lenses that Argonaut provides instead of the Scalaz lenses? If so, working with traversals is a lot nicer:

import argonaut._, Argonaut._
import monocle.function.{ each, index }, monocle.std.list._
import scalaz._, Scalaz._

val doc = """{
  "hashtags": [
    { "indices": [0, 3], "text": "foo" },
    { "indices": [3, 6], "text": "bar" }
  ]
}"""

val \/-(json) = Parse.parse(doc)

val lens = jObjectPrism
  .composeOptional(index("hashtags"))
  .composePrism(jArrayPrism)
  .composeTraversal(each[List[Json], Json])
  .composePrism(jObjectPrism)
  .composeOptional(index("text"))
  .composePrism(jStringPrism)

And then:

scala> lens.getAll(json)
res0: List[argonaut.Argonaut.JsonString] = List(foo, bar)

scala> lens.modify(_ + " (new)")(json).spaces2
res1: String =
{
  "hashtags" : [
    {
      "indices" : [
        0,
        3
      ],
      "text" : "foo (new)"
    },
    {
      "indices" : [
        3,
        6
      ],
      "text" : "bar (new)"
    }
  ]
}

And so on. You could do something similar with Scalaz lenses but it would take more work.

Upvotes: 3

Related Questions