Parse JSON array using Scala Argonaut
Asked Answered
L

3

7

I'm using Scala & Argonaut, trying to parse the following JSON:

[
    {
        "name": "apple",
        "type": "fruit",
        "size": 3
    },
    {
        "name": "jam",
        "type": "condiment",
        "size": 5
    },
    {
        "name": "beef",
        "type": "meat",
        "size": 1
    }
]

And struggling to work out how to iterate and extract the values into a List[MyType] where MyType will have name, type and size properties.

I will post more specific code soon (i have tried many things), but basically I'm looking to understand how the cursor works, and how to iterate through arrays etc. I have tried using \\ (downArray) to move to the head of the array, then :->- to iterate through the array, then --\ (downField) is not available (at least IntelliJ doesn't think so). So the question is how do i:

  • navigate to the array
  • iterate through the array (and know when I'm done)
  • extract string, integer etc. values for each field - jdecode[String]? as[String]?
Livvi answered 18/3, 2014 at 15:32 Comment(2)
What did you try? Can you show your code?Paphos
First of all, your JSON is invalid. Maybe this causes errors? Or what's your plan? See jsonlint.com for JSON validation.Gastric
S
6

The easiest way to do this is to define a codec for MyType. The compiler will then happily construct a decoder for List[MyType], etc. I'll use a plain class here (not a case class) to make it clear what's happening:

class MyType(val name: String, val tpe: String, val size: Int)

import argonaut._, Argonaut._

implicit def MyTypeCodec: CodecJson[MyType] = codec3(
  (name: String, tpe: String, size: Int) => new MyType(name, tpe, size),
  (myType: MyType) => (myType.name, myType.tpe, myType.size)
)("name", "type", "size")

codec3 takes two parameter lists. The first has two parameters, which allow you to tell how to create an instance of MyType from a Tuple3 and vice versa. The second parameter list lets you specify the names of the fields.

Now you can just write something like the following (if json is your string):

Parse.decodeValidation[List[MyType]](json)

And you're done.

Shearin answered 27/3, 2014 at 0:53 Comment(1)
I would suggest to use casecodec instead of codec implicit def MyTypeCodec = casecodec3(MyType.apply, MyType.unapply)("name", "type", "size")Flaw
T
0

Since you don't need to encode and are only looking at decoding, you can do as suggested by Travis, but by implementing another implicit: MyTypeDecodeJson

implicit def MyTypeDecodeJson: DecodeJson[MyType] = DecodeJson(
    raw => for {
    name     <- raw.get[String]("name")
    type     <- raw.get[String]("type")
    size     <- raw.get[Int]("size")
  } yield MyType(name, type, size))

Then to parse your list:

Parse.decodeValidation[List[MyType]](jsonString)
Throve answered 9/2, 2016 at 8:15 Comment(0)
A
0

Assuming MyType is a case class, the following works too:

case class MyType(name: String, type: String, size: Int)

object MyType {
    implicit val createCodecJson: CodecJson[MyType] = CodecJson.casecodec3(apply, unapply)(
        "name",
        "type",
        "size"
    )
}
Antioch answered 2/9, 2020 at 20:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.