문제

I need to convert this JSON

{ "matchItem": { "collection": { "fieldName": [ "value1", "value2" ] } } }

to this MongoDB projection:

{ "collection": { "$elemMatch": { "fieldName": "value1", "fieldName": "value2" } }

Here below is my code:

def toProjection(json: JsValue) = {
  json.getOpt(__ \ "matchItem").map { value =>
    val obj = value.as[JsObject]
    for (key <- obj.keys) { obj.getOpt(__ \ key).map { matchObj =>
      for (matchKey <- matchObj.as[JsObject].keys) { matchObj.getOpt(__ \ matchKey).map { items =>
        val fields = items.as[Seq[JsValue]].map(item => (matchKey -> item))
        seq += key -> Json.obj("$elemMatch" -> Json.obj(fields))
      }}
    }}
  }
  if (seq.length > 0) JsObject(seq) else json
}

val json = """{ "matchItem": { "collection": { "fieldName": [ "value1", "value2" ] } } }"""
val proj = toProjection(json)

This code does not compile and I always get the following error message:

[error] /home/j3d/Projects/test/app/services/Test.scala:82: type mismatch;
[error]  found   : Seq[(String, play.api.libs.json.JsValue)]
[error]  required: (String, play.api.libs.json.Json.JsValueWrapper)
[error]                 seq += fieldMaps.fromPublic(key) -> Json.obj("$elemMatch" -> Json.obj(fields))
[error]                                                                                       ^

I'm a bit lost. I know Json.obj is a helper method to build JsObject instances:

JsObject(Seq(
  "key1" -> JsString("value1"),
  "key2" -> JsString("value2")
  ...
))

... is equivalent to:

Json.obj("key1" -> "value1", "key2" -> "value2")

In my code above, the fields value is a Seq[(String, play.api.libs.json.JsValue)]... so I don't understand why it doesn't work. Any help would be really appreciated.

도움이 되었습니까?

해결책

Here is the solution:

def toProjection(json: JsValue) = {
  json.getOpt(__ \ "matchItem").map { value =>
    val obj = value.as[JsObject]
    for (key <- obj.keys) { obj.getOpt(__ \ key).map { matchObj =>
      for (matchKey <- matchObj.as[JsObject].keys) { matchObj.getOpt(__ \ matchKey).map { items =>
        val fields = items.as[Seq[JsValue]].map(item => (matchKey -> item))
     // seq += key -> Json.obj("$elemMatch" -> Json.obj(fields))
        seq += key -> Json.obj("$elemMatch" -> JsObject(fields))
      }}
    }}
  }
  if (seq.length > 0) JsObject(seq) else json
}

Replacing Json.obj(fields) with JsObject(fields) does the trick.

I hope it helps.

다른 팁

Ended up here after implementing my own solution and thinking there must be an easier way. Looks like there isnt. And here's yet another solution that appears to work:

def flatten(update: JsObject): JsObject = {
    def f(key: String, value: JsValue): Seq[(String, JsValue)] = {
      value match {
        case o: JsObject => o.fields.flatMap(kv => f(s"$key.${kv._1}", kv._2))
        case v: JsValue => Seq(key -> v)
      }
    }
    JsObject(update.fields.flatMap(kv => f(kv._1, kv._2)))
  }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top