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

com.rojoma.json.v3.codec.Path.scala Maven / Gradle / Ivy

The newest version!
package com.rojoma.json.v3
package codec

import ast._
import util.WrapperJsonCodec
import `-impl`.codec.EntryLike

// This doesn't feel like it belongs in "codec"

/** Representation of a particular JSON path */
class Path(val toList: List[Path.Entry]) extends AnyVal {
  override def toString = Path.asString(toList)

  def prepend(prefix: Path.Entry) = new Path(prefix :: toList)
}

object Path {
  val empty = new Path(Nil)

  def apply(entry: EntryLike*) = new Path(entry.map(_.toEntry).toList)

  sealed trait Entry
  case class Index(index: Int) extends Entry
  case class Field(field: String) extends Entry
  object Entry {
    implicit val jCodec: JsonEncode[Entry] with JsonDecode[Entry] = new JsonEncode[Entry] with JsonDecode[Entry] {
      def encode(e: Entry) = e match {
        case Index(i) => JNumber(i)
        case Field(s) => JString(s)
      }
      def decode(x: JValue) = x match {
        case JString(s) => Right(Field(s))
        case n: JNumber => Right(Index(n.toInt))
        case other =>
          Left(
            DecodeError.Multiple(List(
                                   DecodeError.InvalidType(JString, x.jsonType),
                                   DecodeError.InvalidType(JNumber, x.jsonType))))
      }
    }
  }

  // produces a jq-style path spec
  def asString(xs: List[Entry]) = {
    val sb = new StringBuffer(".")

    def appendEntry(e: Entry, isFirst: Boolean) {
      e match {
        case Field(f) =>
          if(isSimple(f)) {
            if(isFirst) sb.append(f)
            else sb.append('.').append(f)
          } else {
            sb.append('[').append(JString(f)).append(']')
          }
        case Index(i) =>
          sb.append('[').append(i).append(']')
      }
    }

    xs match {
      case e :: es =>
        appendEntry(e, true)
        for(e <- es) appendEntry(e, false)
      case Nil =>
        // nothing
    }
    sb.toString
  }

  private def isSimple(f: String): Boolean =
    f.nonEmpty && Character.isJavaIdentifierStart(f.charAt(0)) && restIsPart(f)
  private def restIsPart(f: String): Boolean = {
    var i = 1
    while(i < f.length) {
      if(!Character.isJavaIdentifierPart(f.charAt(i))) return false
      i += 1
    }
    true
  }

  private def create(xs: List[Entry]) = new Path(xs) // helping out type inference
  implicit val jCodec = WrapperJsonCodec[Path](create, _.toList)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy