Skip to content

Commit 978383c

Browse files
author
Boris Malensek
committed
Adds Play-JSON support.
1 parent a86e168 commit 978383c

File tree

4 files changed

+222
-26
lines changed

4 files changed

+222
-26
lines changed

src/main/scala/org/zalando/jsonapi/json/playjson/PlayJsonJsonapiFormat.scala

Lines changed: 91 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ trait PlayJsonJsonapiFormat {
3434
JsArray(values)
3535
}
3636

37-
override def reads(json: JsValue): JsResult[Data] = ???
37+
override def reads(json: JsValue): JsResult[Data] = {
38+
json.asOpt[ResourceObject] match {
39+
case Some(ro) JsSuccess(ro)
40+
case None JsSuccess(ResourceObjects(json.as[ImmutableSeq[ResourceObject]]))
41+
}
42+
}
3843
}
3944

4045
/**
@@ -85,7 +90,22 @@ trait PlayJsonJsonapiFormat {
8590
JsObject(fields)
8691
}
8792

88-
override def reads(json: JsValue): JsResult[Meta] = ???
93+
override def reads(json: JsValue): JsResult[Meta] = json match {
94+
case JsObject(fields)
95+
fields.foldLeft[JsResult[Meta]](JsSuccess(Vector.empty)) {
96+
case (acc, (name, jsValue)) (acc, jsValue.validate[JsonApiObject.Value]) match {
97+
case (JsSuccess(metaProps, _), JsSuccess(value, _))
98+
JsSuccess(metaProps :+ MetaProperty(name, value))
99+
case (JsSuccess(_, _), JsError(errors))
100+
JsError(Seq(JsPath \ name -> errors.flatMap(_._2)))
101+
case (e: JsError, s: JsSuccess[_])
102+
e
103+
case (e: JsError, JsError(errors))
104+
e ++ JsError(Seq(JsPath \ name -> errors.flatMap(_._2)))
105+
}
106+
}
107+
case _ JsError("error.expected.jsobject")
108+
}
89109
}
90110

91111
/**
@@ -97,7 +117,22 @@ trait PlayJsonJsonapiFormat {
97117
JsObject(fields)
98118
}
99119

100-
override def reads(json: JsValue): JsResult[JsonApi] = ???
120+
override def reads(json: JsValue): JsResult[JsonApi] = json match {
121+
case JsObject(fields)
122+
fields.foldLeft[JsResult[JsonApi]](JsSuccess(Vector.empty)) {
123+
case (acc, (name, jsValue)) (acc, jsValue.validate[JsonApiObject.Value]) match {
124+
case (JsSuccess(jsonApiProps, _), JsSuccess(value, _))
125+
JsSuccess(jsonApiProps :+ JsonApiProperty(name, value))
126+
case (JsSuccess(_, _), JsError(errors))
127+
JsError(Seq(JsPath \ name -> errors.flatMap(_._2)))
128+
case (e: JsError, s: JsSuccess[_])
129+
e
130+
case (e: JsError, JsError(errors))
131+
e ++ JsError(Seq(JsPath \ name -> errors.flatMap(_._2)))
132+
}
133+
}
134+
case _ JsError("error.expected.jsobject")
135+
}
101136
}
102137

103138
/**
@@ -109,7 +144,21 @@ trait PlayJsonJsonapiFormat {
109144
JsArray(fields)
110145
}
111146

112-
override def reads(json: JsValue): JsResult[Errors] = ???
147+
override def reads(json: JsValue): JsResult[Errors] = json match {
148+
case JsArray(a) a.foldLeft[JsResult[Errors]](JsSuccess(Vector.empty)) {
149+
case (acc, jsValue) (acc, jsValue.validate[Error]) match {
150+
case (JsSuccess(errs, _), JsSuccess(value, _))
151+
JsSuccess(errs :+ value)
152+
case (JsSuccess(_, _), JsError(errors))
153+
JsError(errors)
154+
case (e: JsError, s: JsSuccess[_])
155+
e
156+
case (e: JsError, JsError(errors))
157+
e ++ JsError(errors)
158+
}
159+
}
160+
case _ JsError("error.expected.jsarray")
161+
}
113162
}
114163

115164
/**
@@ -150,12 +199,32 @@ trait PlayJsonJsonapiFormat {
150199
case JsonApiObject.StringValue(s) JsString(s)
151200
case JsonApiObject.NumberValue(n) JsNumber(n)
152201
case JsonApiObject.BooleanValue(b) JsBoolean(b)
153-
case JsonApiObject.JsObjectValue(o) JsObject(o.map (a a.name -> Json.toJson[JsonApiObject.Value](a.value)))
202+
case JsonApiObject.JsObjectValue(o) JsObject(o.map (a a.name -> Json.toJson(a.value)))
154203
case JsonApiObject.JsArrayValue(a) JsArray(a.map(v Json.toJson[JsonApiObject.Value](v)))
155204
case JsonApiObject.NullValue JsNull
156205
}
157206

158-
override def reads(json: JsValue): JsResult[Value] = ???
207+
override def reads(json: JsValue): JsResult[Value] = json match {
208+
case JsString(s) JsSuccess(JsonApiObject.StringValue(s))
209+
case JsNumber(n) JsSuccess(JsonApiObject.NumberValue(n))
210+
case JsBoolean(b) JsSuccess(JsonApiObject.BooleanValue(b))
211+
case JsObject(o) {
212+
val attrs = o.map(keyValue {
213+
val (key, jsValue) = keyValue
214+
val read = reads(jsValue).getOrElse(JsonApiObject.NullValue)
215+
Attribute(key, read)
216+
}).toList
217+
JsSuccess(JsonApiObject.JsObjectValue(attrs))
218+
}
219+
case JsArray(a) {
220+
val arrayValues = a.map(jsValue {
221+
reads(jsValue).getOrElse(JsonApiObject.NullValue)
222+
}).toList
223+
JsSuccess(JsonApiObject.JsArrayValue(arrayValues))
224+
}
225+
case JsNull JsSuccess(JsonApiObject.NullValue)
226+
case _ JsError("error.expected.jsonapivalue")
227+
}
159228
}
160229

161230
/**
@@ -177,7 +246,20 @@ trait PlayJsonJsonapiFormat {
177246
JsObject(fields)
178247
}
179248

180-
override def reads(json: JsValue): JsResult[Links] = ???
249+
override def reads(json: JsValue): JsResult[Links] = json match {
250+
case JsObject(o) JsSuccess(o.map { keyValue
251+
keyValue match {
252+
case (FieldNames.`about`, JsString(u)) Links.About(u)
253+
case (FieldNames.`first`, JsString(u)) Links.First(u)
254+
case (FieldNames.`last`, JsString(u)) Links.Last(u)
255+
case (FieldNames.`next`, JsString(u)) Links.Next(u)
256+
case (FieldNames.`prev`, JsString(u)) Links.Prev(u)
257+
case (FieldNames.`related`, JsString(u)) Links.Related(u)
258+
case (FieldNames.`self`, JsString(u)) Links.Self(u)
259+
}
260+
}.toVector)
261+
case _ JsError("error.expected.links")
262+
}
181263
}
182264

183265
/**
@@ -189,6 +271,7 @@ trait PlayJsonJsonapiFormat {
189271
JsArray(objects)
190272
}
191273

192-
override def reads(json: JsValue): JsResult[Included] = ???
274+
override def reads(json: JsValue): JsResult[Included] =
275+
JsSuccess(Included(ResourceObjects(json.as[ImmutableSeq[ResourceObject]])))
193276
}
194277
}

src/test/scala/org/zalando/jsonapi/json/JsonBaseSpec.scala

Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,12 @@ trait JsonBaseSpec[JsonBaseType] extends WordSpec {
3232

3333
protected lazy val rootObjectWithJsonApiObjectJson = parseJson(rootObjectWithJsonApiObjectJsonString)
3434

35+
protected lazy val rootObjectWithRelationshipsJson = parseJson(rootObjectWithRelationshipsJsonString)
36+
3537
protected lazy val resourceObjectWithRelationshipsJson = parseJson(resourceObjectWithRelationshipsJsonString)
3638

39+
protected lazy val relationshipsJson = parseJson(relationshipsJsonString)
40+
3741
protected lazy val resourceObjectWithEmptyRelationshipsJson = parseJson(resourceObjectWithEmptyRelationshipsJsonString)
3842

3943
protected lazy val attributesJsonString =
@@ -321,7 +325,7 @@ trait JsonBaseSpec[JsonBaseType] extends WordSpec {
321325
))
322326
)
323327

324-
protected lazy val resourceObjectWithRelationshipsJsonString =
328+
protected lazy val rootObjectWithRelationshipsJsonString =
325329
"""
326330
|{
327331
| "data": [{
@@ -345,20 +349,77 @@ trait JsonBaseSpec[JsonBaseType] extends WordSpec {
345349
|}
346350
""".stripMargin
347351

348-
protected lazy val resourceObjectWithRelationships = RootObject(Some(
349-
ResourceObjects(List(
350-
ResourceObject(
351-
`type` = "person",
352-
id = "1",
353-
attributes = Some(List(Attribute("name", StringValue("foobar")))),
354-
relationships =
355-
Some(Map("father" -> Relationship(
356-
data = Some(ResourceObject(`type` = "person", id = "2")),
357-
links = Some(List(Links.Self("http://link.to.self")))
358-
)))
359-
)
360-
))
361-
))
352+
protected lazy val RootObjectWithRelationships = RootObject(
353+
data = Some(
354+
ResourceObjects(List(
355+
ResourceObject(
356+
`type` = "person",
357+
id = "1",
358+
attributes = Some(List(Attribute("name", StringValue("foobar")))),
359+
relationships =
360+
Some(Map("father" -> Relationship(
361+
data = Some(ResourceObject(`type` = "person", id = "2")),
362+
links = Some(List(Links.Self("http://link.to.self")))
363+
)))
364+
)
365+
))
366+
)
367+
)
368+
369+
protected lazy val resourceObjectWithRelationshipsJsonString =
370+
"""
371+
|{
372+
| "type": "person",
373+
| "id": "1",
374+
| "attributes": {
375+
| "name": "foobar"
376+
| },
377+
| "relationships": {
378+
| "father": {
379+
| "data": {
380+
| "id": "2",
381+
| "type": "person"
382+
| },
383+
| "links": {
384+
| "self": "http://link.to.self"
385+
| }
386+
| }
387+
| }
388+
|}
389+
""".stripMargin
390+
391+
protected lazy val resourceObjectWithRelationshipsObject = ResourceObject(
392+
`type` = "person",
393+
id = "1",
394+
attributes = Some(List(Attribute("name", StringValue("foobar")))),
395+
relationships =
396+
Some(Map("father" -> Relationship(
397+
data = Some(ResourceObject(`type` = "person", id = "2")),
398+
links = Some(List(Links.Self("http://link.to.self")))
399+
)))
400+
)
401+
402+
protected lazy val relationshipsJsonString =
403+
"""
404+
|{
405+
| "father": {
406+
| "data": {
407+
| "id": "2",
408+
| "type": "person"
409+
| },
410+
| "links": {
411+
| "self": "http://link.to.self"
412+
| }
413+
| }
414+
|}
415+
""".stripMargin
416+
417+
protected lazy val relationshipsObject = Map(
418+
"father" -> Relationship(
419+
data = Some(ResourceObject(`type` = "person", id = "2")),
420+
links = Some(List(Links.Self("http://link.to.self")))
421+
)
422+
)
362423

363424
protected lazy val resourceObjectWithEmptyRelationshipsJsonString =
364425
"""

src/test/scala/org/zalando/jsonapi/json/playjson/PlayJsonJsonapiFormatSpec.scala

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package org.zalando.jsonapi.json.playjson
22

33
import org.scalatest.MustMatchers
44
import org.zalando.jsonapi.json.JsonBaseSpec
5+
import org.zalando.jsonapi.model.RootObject.ResourceObject
6+
import org.zalando.jsonapi.model._
57
import play.api.libs.json._
68

79
class PlayJsonJsonapiFormatSpec extends JsonBaseSpec[JsValue] with MustMatchers with PlayJsonJsonapiFormat {
@@ -47,11 +49,61 @@ class PlayJsonJsonapiFormatSpec extends JsonBaseSpec[JsValue] with MustMatchers
4749
Json.toJson(rootObjectWithJsonApiObject) mustEqual rootObjectWithJsonApiObjectJson
4850
}
4951
"transform relationship object correctly" in {
50-
Json.toJson(resourceObjectWithRelationships) mustEqual resourceObjectWithRelationshipsJson
52+
Json.toJson(RootObjectWithRelationships) mustEqual rootObjectWithRelationshipsJson
5153
}
5254
"transform empty relationship object correctly" in {
5355
Json.toJson(resourceObjectWithEmptyRelationshipsObject) mustEqual resourceObjectWithEmptyRelationshipsJson
5456
}
5557
}
58+
"deserializing Jsonapi" must {
59+
"transform attributes correctly" in {
60+
Json.fromJson[Attributes](attributesJson) mustEqual JsSuccess(attributes)
61+
}
62+
"transform resource object correctly" in {
63+
Json.fromJson[RootObject](rootObjectWithResourceObjectWithoutAttributesJson) mustEqual JsSuccess(rootObjectWithResourceObjectWithoutAttributes)
64+
}
65+
"transform a list of resource objects correctly" in {
66+
Json.fromJson[RootObject](rootObjectWithResourceObjectsJson) mustEqual JsSuccess(rootObjectWithResourceObjects)
67+
}
68+
"transform resource identifier object correctly" in {
69+
Json.fromJson[RootObject](rootObjectWithResourceIdentifierObjectJson) mustEqual JsSuccess(rootObjectWithResourceIdentifierObject)
70+
}
71+
"transform a list of resource identifier objects correctly" in {
72+
Json.fromJson[RootObject](rootObjectWithResourceIdentifierObjectsJson) mustEqual JsSuccess(rootObjectWithResourceIdentifierObjects)
73+
}
74+
"transform all link types correctly" in {
75+
Json.fromJson[RootObject](rootObjectWithResourceObjectsWithAllLinksJson) mustEqual JsSuccess(rootObjectWithResourceObjectsWithAllLinks)
76+
}
77+
"transform all meta object inside resource object correctly" in {
78+
Json.fromJson[RootObject](rootObjectWithResourceObjectsWithMetaJson) mustEqual JsSuccess(rootObjectWithResourceObjectsWithMeta)
79+
}
80+
"transform a meta object in root object correctly" in {
81+
Json.fromJson[RootObject](rootObjectWithMetaJson) mustEqual JsSuccess(rootObjectWithMeta)
82+
}
83+
"transform error object correctly" in {
84+
Json.fromJson[RootObject](rootObjectWithErrorsJson) mustEqual JsSuccess(rootObjectWithErrorsObject)
85+
}
86+
"transform error object without links and meta and source correctly" in {
87+
Json.fromJson[RootObject](rootObjectWithErrorsNoneJson) mustEqual JsSuccess(rootObjectWithErrorsNoneObject)
88+
}
89+
"transform included object correctly" in {
90+
Json.fromJson[RootObject](rootObjectWithIncludedJson) mustEqual JsSuccess(rootObjectWithIncluded)
91+
}
92+
"transform jsonapi object correctly" in {
93+
Json.fromJson[RootObject](rootObjectWithJsonApiObjectJson) mustEqual JsSuccess(rootObjectWithJsonApiObject)
94+
}
95+
"transform resource object with relationships correctly" in {
96+
Json.fromJson[ResourceObject](resourceObjectWithRelationshipsJson) mustEqual JsSuccess(resourceObjectWithRelationshipsObject)
97+
}
98+
"transform relationships object correctly" in {
99+
Json.fromJson[Relationships](relationshipsJson) mustEqual JsSuccess(relationshipsObject)
100+
}
101+
"transform root object with relationship correctly" in {
102+
Json.fromJson[RootObject](rootObjectWithRelationshipsJson) mustEqual JsSuccess(RootObjectWithRelationships)
103+
}
104+
"transform empty relationship object correctly" in {
105+
Json.fromJson[RootObject](resourceObjectWithEmptyRelationshipsJson) mustEqual JsSuccess(resourceObjectWithEmptyRelationshipsObject)
106+
}
107+
}
56108
}
57109
}

src/test/scala/org/zalando/jsonapi/json/sprayjson/SprayJsonJsonapiFormatSpec.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class SprayJsonJsonapiFormatSpec extends JsonBaseSpec[JsValue] with MustMatchers
4848
rootObjectWithJsonApiObject.toJson mustEqual rootObjectWithJsonApiObjectJson
4949
}
5050
"transform relationship object correctly" in {
51-
resourceObjectWithRelationships.toJson mustEqual resourceObjectWithRelationshipsJson
51+
RootObjectWithRelationships.toJson mustEqual rootObjectWithRelationshipsJson
5252
}
5353
"transform empty relationship object correctly" in {
5454
resourceObjectWithEmptyRelationshipsObject.toJson mustEqual resourceObjectWithEmptyRelationshipsJson
@@ -92,7 +92,7 @@ class SprayJsonJsonapiFormatSpec extends JsonBaseSpec[JsValue] with MustMatchers
9292
rootObjectWithJsonApiObjectJson.convertTo[RootObject] === rootObjectWithJsonApiObject
9393
}
9494
"transform relationship object correctly" in {
95-
resourceObjectWithRelationshipsJson.convertTo[RootObject] === resourceObjectWithRelationships
95+
rootObjectWithRelationshipsJson.convertTo[RootObject] === RootObjectWithRelationships
9696
}
9797
"transform empty relationship object correctly" in {
9898
resourceObjectWithEmptyRelationshipsJson.convertTo[RootObject] === resourceObjectWithEmptyRelationshipsObject

0 commit comments

Comments
 (0)