I need a JSON structure that contains a name/value pair... and the value might be either a String
or a List[String]
:
{
"name":"id",
"value":"55e9cf3e0100003700dd2134"
}
{
"name":"id",
"value":["55e9cf3e0100003700dd2134","55e9e3430100004000d73b59"]
}
Here below is the class I use to deal with such a structure:
class NameValue protected(protected var json: JsValue) {
def name = json as (__ \ 'name).read[String]
def name_= (v: String) = setValue((__ \ 'name), Json.toJson(v))
def value = json \ "value" match {
case v: JsString => v.value
case v => v.as[List[String]].head
}
def value_= (v: String) = setValue((__ \ 'value), Json.toJson(v))
def multiValue = json \ "value" match {
case v: JsString => List(v.value)
case v => v.as[List[String]]
}
def multiValue_= (v: List[String]) = setValue((__ \ 'value), Json.toJson(v))
}
The value
method returns
String
List[String]
The multiValue
method returns
List[String]
List[String]
containing one element in case of String
Here below is the companion object that creates NameValue
instances and validates its JSON:
object NameValue {
import play.api.libs.functional.syntax._
import play.api.libs.json.Reads._
def apply(json: JsValue): JsResult[NameValue] = {
validateNameValue.reads(json).fold(
valid = { validated => JsSuccess(new NameValue(validated)) },
invalid = { errors => JsError(errors) }
)
}
def apply(name: String, value: String): NameValue = new NameValue(
nameValueWrites.writes(name, Some(value), None)
)
def apply(name: String, multiValue: List[String]): NameValue = new NameValue(
nameValueWrites.writes(name, None, Some(multiValue))
)
def unapply(nameValue: NameValue) = {
if (nameValue eq null) None
else {
val multiValue = rameValue.multiValue
Some((
nameValue.name,
if (multiValue.length > 1) multiValue else multiValue.head
))
}
}
implicit val nameValueFormat = new Format[NameValue] {
def reads(json: JsValue) = NameValue(json)
def writes(nameValue: NameValue) = nameValue.json
}
private val nameValueWrites = (
(__ \ 'name).write[String] ~
(__ \ 'value).writeNullable[String] ~
(__ \ 'value).writeNullable[List[String]]
).tupled
private val multiValue: Reads[JsArray] = {
(__ \ 'value).json.pick[JsArray] andThen verifying[JsArray](_.value.nonEmpty)
}
val validateNameValue = (
((__ \ 'name).json.pickBranch) ~
((__ \ 'value).json.pickBranch)
).reduce
// how do I integrate this in `validateNameValue`?
val validateNameValue2 = (
((__ \ 'name).json.pickBranch) ~
((__ \ 'value).json.copyFrom(multiValue))
).reduce
}
Everything works fine... but I'd like to have just one validator regardless of whether the value
attribute is a String
or a List[String]
. How do I create a single validator that replaces validateNameValue
and validateNameValue2
?
Differentiate value into String and a list of String is not a good practice. A List can also contain a single element.
In addition, you can use Json.format to get rid of boilerplate code of defining reader and writer for a case class.
import play.api.libs.json.Json
object NameValue {
implicit val format = Json.format[NameValue]
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.