I have a list of courses from coursera api:
{"elements":[
{"id":69,
"shortName":"contraception",
"name":"Contraception: Choices, Culture and Consequences",
"links":{}
},
...
]
}
I want to convert it to a document that look like so ( i use <--- arrrows as comments):
{"Courses":[
{
"Type" : "Course",
"Title" : "contraception" <---- short name
"Content" : {"id":69, <---- the original course
"shortName":"contraception",
"name":"Contraception: Choices, Culture and Consequences",
"links":{}
}
},
...
]}
Is it possible to perform this with json only api from play? Here is how I do it presently (with conversion to scala lists).
val courses = (response.json \ "elements")
.as[List[JsValue]]
.map { course =>
// This is how we want our document to look
Json.obj(
"Type" -> "Course",
"Provider" -> "Coursera",
"Title" -> (course \ "name"),
"Content" -> course
)
}
// then put this into the final json object with "Courses" ...
This can be done with Json Transformers API:
It's more messy, but this is as close as I got:
val json = Json.parse( """
{ "elements":[
{"id":69,
"shortName":"contraception",
"name":"Contraception: Choices, Culture and Consequences",
"links":{}
},
{"id":100,
"shortName":"test",
"name":"Test of name",
"links":{}
}
]
}
""")
// Maps single course to app course
val apiCourseToAppCourse = (
(__ \ 'Type).json.put(JsString("Course")) and
(__ \ 'Title).json.copyFrom((__ \ 'shortName).json.pick) and
(__ \ 'Content).json.copyFrom(__.json.pickBranch)
).reduce
// Maps an array of elements to an array of courses
val apiCoursesToAppCourses = of[JsArray].map {
case JsArray(arr) =>
// Map elements to courses
JsArray(arr.map {
case course: JsObject =>
apiCourseToAppCourse.reads(course).getOrElse(JsString("Failed to read the course"))
})
}
// Maps the API result to a valid result
val apiToCourses = (__ \ 'Courses).json.copyFrom((__ \ 'elements).json.pick(apiCoursesToAppCourses))
// Validate JSON and send result
json.transform(apiToCourses) match {
case JsSuccess(success, p) =>
Ok(Json.toJson(success))
case JsError(errors) =>
Ok(errors + "")
}
You can copy code and put it in a Controller, I have it working. I don't like so much the array mapping but that's the way it's being done in the documentation example.