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

com.spotify.featran.json.Implicits.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2018 Spotify AB.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.spotify.featran.json

import com.spotify.featran.transformers.{MDLRecord, Settings, WeightedLabel}
import com.spotify.featran.{FlatReader, FlatWriter}
import io.circe._
import io.circe.parser.{decode => circeDecode}
import io.circe.syntax._

private[featran] trait Implicits extends Serializable {
  implicit val mdlRecordDecoder: Decoder[MDLRecord[String]] = new Decoder[MDLRecord[String]] {
    override def apply(c: HCursor): Decoder.Result[MDLRecord[String]] =
      c.keys.toList.flatten.headOption match {
        case None => Left(DecodingFailure("", c.history))
        case Some(label) =>
          for {
            num <- c.downField(label).as[Double]
          } yield MDLRecord(label, num)
      }
  }

  implicit val mdlRecordEncoder: Encoder[MDLRecord[String]] = new Encoder[MDLRecord[String]] {
    override def apply(mdlRecord: MDLRecord[String]): Json = Json.obj(
      (mdlRecord.label, Json.fromDoubleOrNull(mdlRecord.value))
    )
  }

  implicit val weightedLabelDecoder: Decoder[WeightedLabel] = new Decoder[WeightedLabel] {
    override def apply(c: HCursor): Decoder.Result[WeightedLabel] =
      c.keys.toList.flatten.headOption match {
        case None => Left(DecodingFailure("", c.history))
        case Some(label) =>
          for {
            num <- c.downField(label).as[Double]
          } yield WeightedLabel(label, num)
      }
  }

  implicit val weightedLabelEncoder: Encoder[WeightedLabel] = new Encoder[WeightedLabel] {
    override def apply(weightedLabel: WeightedLabel): Json = Json.obj(
      (weightedLabel.name, Json.fromDoubleOrNull(weightedLabel.value))
    )
  }

  implicit val genericMapDecoder: Decoder[Map[String, Option[String]]] =
    new Decoder[Map[String, Option[String]]] {
      override def apply(c: HCursor): Decoder.Result[Map[String, Option[String]]] =
        c.keys
          .filter(_.nonEmpty)
          .toRight(DecodingFailure("flat decoding", c.history))
          .map(list => list.map(f => (f, c.downField(f).focus.map(_.noSpaces))).toMap)
    }

  implicit val genericSeqEncoder: Encoder[Seq[(String, Option[Json])]] =
    new Encoder[Seq[(String, Option[Json])]] {
      override def apply(seq: Seq[(String, Option[Json])]): Json =
        Json.obj(seq.collect { case (n, Some(json)) => (n, json) }: _*)
    }

  implicit val settingsDecoder: Decoder[Settings] =
    Decoder.forProduct5("cls", "name", "params", "featureNames", "aggregators")(Settings.apply)

  implicit val settingsEncoder: Encoder[Settings] =
    Encoder.forProduct5("cls", "name", "params", "featureNames", "aggregators") { s =>
      (s.cls, s.name, s.params, s.featureNames, s.aggregators)
    }

  implicit val jsonFlatReader: FlatReader[String] = new FlatReader[String] {
    private def toFeature[T: Decoder](name: String): String => Option[T] =
      json =>
        circeDecode[Map[String, Option[String]]](json).toOption
          .flatMap { map =>
            map
              .get(name)
              .flatten
              .flatMap(o => circeDecode[T](o).toOption)
          }

    override def readDouble(name: String): String => Option[Double] = toFeature[Double](name)

    override def readMdlRecord(name: String): String => Option[MDLRecord[String]] =
      toFeature[MDLRecord[String]](name)

    override def readWeightedLabel(name: String): String => Option[List[WeightedLabel]] =
      toFeature[List[WeightedLabel]](name)

    override def readDoubles(name: String): String => Option[Seq[Double]] =
      toFeature[Seq[Double]](name)

    override def readDoubleArray(name: String): String => Option[Array[Double]] =
      toFeature[Array[Double]](name)

    override def readString(name: String): String => Option[String] = toFeature[String](name)

    override def readStrings(name: String): String => Option[Seq[String]] =
      toFeature[Seq[String]](name)
  }

  implicit val jsonFlatWriter: FlatWriter[String] = new FlatWriter[String] {
    override type IF = (String, Option[Json])

    override def writeDouble(name: String): Option[Double] => (String, Option[Json]) =
      (v: Option[Double]) => (name, v.map(_.asJson))

    override def writeMdlRecord(name: String): Option[MDLRecord[String]] => (String, Option[Json]) =
      (v: Option[MDLRecord[String]]) => (name, v.map(_.asJson))

    override def writeWeightedLabel(
      name: String
    ): Option[Seq[WeightedLabel]] => (String, Option[Json]) =
      (v: Option[Seq[WeightedLabel]]) => (name, v.map(_.asJson))

    override def writeDoubles(name: String): Option[Seq[Double]] => (String, Option[Json]) =
      (v: Option[Seq[Double]]) => (name, v.map(_.asJson))

    override def writeDoubleArray(name: String): Option[Array[Double]] => (String, Option[Json]) =
      (v: Option[Array[Double]]) => (name, v.map(_.asJson))

    override def writeString(name: String): Option[String] => (String, Option[Json]) =
      (v: Option[String]) => (name, v.map(_.asJson))

    override def writeStrings(name: String): Option[Seq[String]] => (String, Option[Json]) =
      (v: Option[Seq[String]]) => (name, v.map(_.asJson))

    override def writer: Seq[(String, Option[Json])] => String =
      _.asJson.noSpaces
  }
}

private[featran] object Implicits extends Implicits




© 2015 - 2024 Weber Informatics LLC | Privacy Policy