All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.specs2.json.Json.scala Maven / Gradle / Ivy

package org.specs2
package json

import util.parsing.json._
import util.matching.Regex
import text.Regexes._
import text.NotNullStrings._

/**
 * This trait provides utility functions for Json objects
 */
private[specs2]
trait Json {

  /**
   * The string has to be parsed by a brand new parser each time
   * otherwise it will not be thread (see issue #70)
   * @return Some(json) if the string is parsable as a JSON document
   */
  def parse(s: String): Option[JSONType] =
    new Parser {
    def parseRaw(input : String) : Option[JSONType] =
      phrase(root)(new lexical.Scanner(input)) match {
        case Success(result, _) => Some(result)
        case _ => None
      }
  }.parseRaw(s)

  /**
   * @return the list of pairs in the json document where the value is a terminal type
   */
  def terminalPairs(json: JSONType): Seq[(Any, Any)] = collect(json)(keyedValues = {
    case (key, value) => Seq((key,value))
    case other        => Nil
  })

  /**
   * @return the list of values in the json document where the value is a terminal type
   */
  def terminalValues(json: JSONType): Seq[Any] = collect(json)(keyedValues = {
    case (key, value) => Seq(value)
    case other        => Seq(other)
  })

  /**
   * @return all the JSON objects or values that are addressed by a key in the document.
   *         if there is only one it is returned directly, otherwise the elements are put in a JSONArray
   */
  def findDeep(key: String, json: JSONType): Option[JSONType] = findDeep(json, (k: Any) => k == key)
  /**
   * @return findDeep with a Regex
   */
  def findDeep(key: Regex, json: JSONType): Option[JSONType] = findDeep(json, (k: Any) => key matches k.toString)
  /**
   * @return findDeep with a Regex or a String
   */
  def findDeep(key: Any, json: JSONType): Option[JSONType] = key match {
    case k: Regex => findDeep(k, json)
    case other    => findDeep(other.notNull, json)
  }

  /**
   * @return findDeep with a key equality function
   */
  def findDeep(json: JSONType, targetKey: Any => Boolean): Option[JSONType] = {
    val seq = collect(json)( keyedValues = {
      case (k, v) if targetKey(k) => Seq(v)
      case other                => Nil
    }, keyedObjects = {
      case (k, o) if targetKey(k) => Seq(o)
      case other                => Nil
    })
    seq match {
      case Nil                   => None
      case (o: JSONType) :: Nil  => Some(o)
      case other                 => Some(JSONArray(seq.toList))
    }
  }

  /**
   * @return the JSON object that's addressed by a key if the document is a Map
   */
  def find(key: String, json: JSONType): Option[JSONType] = json match {
    case JSONObject(map) => map.get(key) map { case (o: JSONType) => o }
    case other           => None
  }

  /**
   * @return the JSON object that's addressed by a key if the document is a Map, and the key matches a regular expression
   */
  def find(keyRegex: Regex, json: JSONType): Option[JSONType] = json match {
    case JSONObject(map) => map.iterator.toSeq.collect { case (k, v) if keyRegex matches k => v }.headOption map { case (o: JSONType) => o }
    case other           => None
  }

  /**
   * @return find with a Regex or a String
   */
  def find(key: Any, json: JSONType): Option[JSONType] = key match {
    case k: Regex => find(k, json)
    case other    => find(other.notNull, json)
  }

  /**
   * generic collect operation to iterate through the JSON tree and get values and objects
   */
  private def collect[T](json: JSONType)(values: Any => Seq[T] = vs,
                                         objects: JSONType => Seq[T] = os,
                                         keyedValues: (Any, Any)  => Seq[T] = kvs,
                                         keyedObjects: (Any, Any) => Seq[T] = kvs): Seq[T] = json match {
    case JSONObject(map) => map.toList.flatMap { v => v match {
        case (k, (o: JSONType)) => objects(o) ++ keyedObjects(k, o) ++ collect(o)(values, objects, keyedValues, keyedObjects)
        case (k, v)             => values(v)  ++ keyedValues(k, v)
      }
    }
    case JSONArray((o: JSONType) :: rest) => objects(o) ++
                                             collect(o)(values, objects, keyedValues, keyedObjects) ++
                                             collect(JSONArray(rest))(values, objects, keyedValues, keyedObjects)
    case JSONArray(other :: rest)         => values(other) ++
                                             collect(JSONArray(rest))(values, objects, keyedValues, keyedObjects)
    case JSONArray(Nil)                   => Nil
  }

  private def vs[T]  = (a: Any) => (Nil:Seq[T])
  private def os[T]  = (a: JSONType) => (Nil:Seq[T])
  private def kvs[T] = (k: Any, v: Any) => (Nil:Seq[T])
}
private[specs2]
object Json extends Json




© 2015 - 2025 Weber Informatics LLC | Privacy Policy