Search code examples
jsonscalafoldleftargonaut

Scala Argonaut folding across a list?


I'm very new to Argonaut (which I'm forced to use since the codebase is older, using an old version of scalaz, and we have no intentions to update it since we're rewriting this outdated code from scratch), and I'm trying to find a way to dump some data into JSON.

I have an case class called Magnitude. For simplicity, define it as:

case class Magnitude(bandname: String)

What I want is a function that takes in a List[Magnitude] and outputs Json that I can chain together with other Json.

Right now, I'm doing:

  def magnitudeFields(magnitudes: List[Magnitude]): Json =
    magnitudes.foldLeft(jEmptyObject) { case (j, m) =>
      ("bandName" := m.bandname) ->: j
    }

and then say I have an instantiation of List[Magnitude]:

val magList = List(Magnitude("R"), Magnitude("J"), Magnitude("_uc"), Magnitude("K"))

when I call magnitudeFields(magList) in a call like:

... previousJsonData ->: ('magnitudes' := magnitudeFields(magList)) ->: nextJsonData ...

I only receive the bandName: K and the other three preceding entries are not part of the JSON output by the function:

"magnitudes" : {
                    "bandName" : "K"
                  },

instead of what I want, which is:

"magnitudes" : {
                    "bandName" : "R"
                    "bandName" : "J"
                    "bandName" : "_uc"
                    "bandName" : "K"
                  },

Any help would be greatly appreciated. I suspect I'm doing something wrong by using jEmptyObject as the default to the foldLeft and may need to use something like jArray at some point (with the function returning List[Json] instead, but I'm not sure how I should be doing this.


Solution

  • You should just define an EncodeJson instance for Magnitude and let Argonaut deal with the rest of the logic:

    import argonaut._, Argonaut._
    
    case class Magnitude(bandname: String)
    
    object Magnitude {
      implicit val encode: EncodeJson[Magnitude] =
        EncodeJson { m =>
          jSingleObject("bandName", jString(m.bandname))
        }
    }
    
    object Main {
      val magList = List(Magnitude("R"), Magnitude("J"), Magnitude("_uc"), Magnitude("K"))
    
      def main(args: Array[String]): Unit = {
        val prefix = "prefix" := "prefix"
        val suffix = "suffix" := "suffix"
        val json = prefix ->: ("magnitudes" := magList) ->: suffix ->: jEmptyObject
    
        println(json.asJson.spaces2)
        //{
        //  "prefix" : "prefix",
        //  "magnitudes" : [
        //  {
        //    "bandName" : "R"
        //  },
        //  {
        //    "bandName" : "J"
        //  },
        //  {
        //    "bandName" : "_uc"
        //  },
        //  {
        //    "bandName" : "K"
        //  }
        //  ],
        //  "suffix" : "suffix"
        //}
      }
    }