Search code examples
arraysjsonscalaencodeargonaut

Scala Argonaut Json Array/Object


I need to create this json object using scala and argonaut:

   "name_value_list":{
      "created_by":{
          "name":"created_by",
          "value":"1"
       },       
       "name":{
          "name":"name",
          "value":"The Name"
       },
       "securitygroup_primary_group":{
           "name":"securitygroup_primary_group",
           "value":""
     }
  }

with scala i'm create this object:

Map("name_value_list" -> (
  Map("created_by"-> Map("name" ->"created_by", "value"->"1")),
  Map("name"-> Map("name" ->"name", "value"->"X")),
  Map("securitygroup_primary_group"-> Map("name" ->"securitygroup_primary_group", "value" ->"")))

But the json generated is this:

   "name_value_list":[{
          "created_by":{
              "name":"created_by",
              "value":"1"
           },       
           "name":{
              "name":"name",
              "value":"The Name"
           },
           "securitygroup_primary_group":{
               "name":"securitygroup_primary_group",
               "value":""
         }
      }]

Can help please?


Solution

  • The JSON generated contains an Array, but you want a Map instead. In order to find out where you have gone wrong, you should investigate the Scala types you have created before the JSON encoding.

    Your code can be refactored to the following:

    val tuple = (
      Map("created_by" -> Map("name" -> "created_by", "value" -> "1")),
      Map("name" -> Map("name" -> "name", "value" -> "X")),
      Map("securitygroup_primary_group" -> Map("name" -> "securitygroup_primary_group", "value" -> ""))
    )
    val toEncode = Map(
      "name_value_list" -> tuple
    )
    

    where tuple is of type Tuple3[Map[String, Map[String, String]], Map[String, Map[String, String]], Map[String, Map[String, String]]].

    Argonaut naturally converts Tuples into Arrays.

    In order to get the JSON you want, you need to change the Tuple into a Map:

    val notATuple = Map(
      "created_by" -> Map("name" -> "created_by", "value" -> "1"),
      "name" -> Map("name" -> "name", "value" -> "X"),
      "securitygroup_primary_group" -> Map("name" -> "securitygroup_primary_group", "value" -> "")
    )
    

    Where the final answer is:

    val toEncode = Map(
      "name_value_list" -> Map(
        "created_by" -> Map("name" -> "created_by", "value" -> "1"),
        "name" -> Map("name" -> "name", "value" -> "X"),
        "securitygroup_primary_group" -> Map("name" -> "securitygroup_primary_group", "value" -> "")
      )
    )
    

    A side note: you may find it easier to construct your data with case classes, and then write Codecs to encode them instead. I find case classes easier to reason about than Maps of Maps of Maps.

    As of Argonaut 6.1-M5, you can use macros to automatically derive codecs from case classes, like so:

    import argonaut._
    implicit val codec = CodecJson.derive[YourCaseClass]