Search code examples
postgresqlscalaanorm

Anorm parsing JSON from PostgreSQL jsonb column


i'm running play 2.3 and have no way to update ... whil i'm trying to parse jsonb column i get the error play - Cannot invoke the action, eventually got an error: java.lang.RuntimeException: UnexpectedNullableFound(ColumnName(.sports,Some(sports)))

my code is :

case class Sport(
    id: UUID,
    name: Option[String]
)

implicit val SportWrites: Writes[Sport] = Json.writes[Sport]
implicit val SportReads: Reads[Sport] = (
    (JsPath \ "f1").read[UUID] and
    (JsPath \ "f2").readNullable[String]
)

case class Member(
    id:UUID,
    name:String,
    sports: List[Sport]
)

def memberParser: RowParser[Member] = {
    get[UUID]("member_id") ~
    get[String]("name") ~
    get[JsValue]("sports") map {
        case(id ~ name ~ sports) => Member(
            id, name
            sports.validate[List[Sport]].getOrElse(List())
        )
    }
}


def getMember():List[Member] = DB.WithConnection{ implicit c =>
    SQL("SELECT m.id, m.name, m.sports::json from member m ").as(memberParser.*)
}

|Sports| is a jsonb postgresql column, if i dont cast this column as ::json i get a TypeDoesNotMatch error.

Json fields are named "f1", "f2", "f3"(...) because i have populated this column with data from another table with JSON_AGG(... so it's postgresql naming.

also this function is working:

def getMemberWithSports():List[Member] = DB.withConnection{ implicit c =>
    SQL("""
        SELECT m.member_id, m.name,
        json_agg(row_to_json(row(s.sport_id, s.name))) as sports
        FROM rank_member m
        LEFT JOIN member_sport ms ON m.member_id = ms.member_id
        LEFT JOIN sport s ON ms.sport_id = s.sport_id
        GROUP BY m.member_id, m.fullname
    """).as(memberParser.*)
} 

the only difference between the results of these two SQL commande (in getMember() & getMemberWithSport() ) is that with the first query i get nothing in sport will the second query (the one that work in play) return JSON populated with null values.


Solution

  • the only workaround i've found for the moment is by adding COALESCE to the SQL query as follow:

    def getMember():List[Member] = DB.WithConnection{ implicit c =>
        SQL("SELECT m.id, m.name, COALESCE(m.sports::json, '[]') as sports from member m ").as(memberParser.*)
    }