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

tethys.readers.instances.SimpleJsonReader.scala Maven / Gradle / Ivy

The newest version!
package tethys.readers.instances

import tethys.JsonReader
import tethys.readers.instances.SimpleJsonReader.FieldDefinition
import tethys.readers.tokens.TokenIterator
import tethys.readers.{FieldName, ReaderError}

import scala.annotation.tailrec
import scala.collection.mutable

private[readers] class SimpleJsonReader[A](
    fields: Array[FieldDefinition[_]],
    mapper: Array[Any] => A,
    strict: Boolean
) extends JsonReader[A] {

  private val defaults: Array[Any] = fields.map(_.defaultValue)

  override def read(it: TokenIterator)(implicit fieldName: FieldName): A = {
    if (!it.currentToken().isObjectStart)
      ReaderError.wrongJson(
        s"Expected object start but found: ${it.currentToken()}"
      )
    else {
      it.nextToken()
      val extracted: Array[Any] = recRead(it, defaults.clone())

      val notExtracted = collectNotExtracted(
        0,
        hasErrors = false,
        extracted,
        new mutable.StringBuilder()
      )
      if (notExtracted.nonEmpty)
        ReaderError.wrongJson(s"Can not extract fields $notExtracted")
      else mapper(extracted)
    }
  }

  @tailrec
  private def collectNotExtracted(
      i: Int,
      hasErrors: Boolean,
      extracted: Array[Any],
      builder: mutable.StringBuilder
  ): String = {
    if (i >= extracted.length) {
      if (hasErrors) builder.append('\'').result()
      else ""
    } else if (extracted(i) == null) {
      if (!hasErrors)
        collectNotExtracted(
          i + 1,
          hasErrors = true,
          extracted,
          builder.append('\'').append(fields(i).name)
        )
      else
        collectNotExtracted(
          i + 1,
          hasErrors = true,
          extracted,
          builder.append("', '").append(fields(i).name)
        )
    } else {
      collectNotExtracted(i + 1, hasErrors, extracted, builder)
    }
  }

  @tailrec
  private def recRead(it: TokenIterator, extracted: Array[Any])(implicit
      fieldName: FieldName
  ): Array[Any] = {
    it.currentToken() match {
      case token if token.isObjectEnd =>
        it.nextToken()
        extracted
      case token if token.isFieldName =>
        val name = it.fieldName()
        recRead(it, extractField(0, name, it.next(), extracted))
      case token =>
        ReaderError.wrongJson(
          s"Expect end of object or field name but '$token' found"
        )
    }
  }

  @tailrec
  private def extractField(
      i: Int,
      name: String,
      it: TokenIterator,
      extracted: Array[Any]
  )(implicit fieldName: FieldName): Array[Any] = {
    if (i >= extracted.length) {
      if (strict) {
        ReaderError.wrongJson(
          s"unexpected field '$name', expected one of ${fields.map(_.name).mkString("'", "', '", "'")}"
        )
      } else {
        it.skipExpression()
        extracted
      }
    } else {
      val field = fields(i)
      if (field.name == name) {
        extracted(i) = field.reader.read(it)(fieldName.appendFieldName(name))
        extracted
      } else {
        extractField(i + 1, name, it, extracted)
      }
    }
  }
}

private[readers] object SimpleJsonReader {
  case class FieldDefinition[A](
      name: String,
      defaultValue: Any,
      reader: JsonReader[A]
  )
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy