Skip to content

Instantly share code, notes, and snippets.

@jgogstad
Last active September 21, 2022 21:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jgogstad/f2833bd076268d6cf902973764b0a099 to your computer and use it in GitHub Desktop.
Save jgogstad/f2833bd076268d6cf902973764b0a099 to your computer and use it in GitHub Desktop.
YAML -> JSON -> HOCON -> CaseClass -> HOCON -> JSON -> YAML using Json4s, moultingyaml and pureconfig
import org.json4s._
import org.json4s.JsonAST.{JArray, JNull, JObject, JNumber}
import net.jcazevedo.moultingyaml._
import net.jcazevedo.moultingyaml.DefaultYamlProtocol._
object YamlJsonProtocol extends CollectionFormats {
implicit object JValueYamlFormat extends YamlFormat[JValue] {
override def write(jv: JValue): YamlValue = jv match {
case JDouble(n) => YamlNumber(n)
case JDecimal(n) => YamlNumber(n)
case JLong(n) => YamlNumber(n)
case JInt(n) => YamlNumber(n)
case JString(s) => YamlString(s)
case a: JArray => seqFormat[JValue].write(a.arr)
case o: JObject => mapFormat[String, JValue].write(o.obj.toMap)
case JBool(b) => YamlBoolean(b)
case JNull => YamlNull
case x => serializationError(s"Unexpected JSON value: $x")
}
override def read(yv: YamlValue): JValue = yv match {
case x: YamlNumber => x.value.toBigIntExact().map(JInt).getOrElse(JDecimal(x.value))
case YamlString(s) => JString(s)
case a: YamlArray => JArray(listFormat[JValue].read(a))
case o: YamlObject => JObject(mapFormat[String, JValue].read(o).toList)
case YamlBoolean(b) => JBool(b)
case YamlNull => JNull
case x => deserializationError(s"Unexpected YAML value: $x")
}
}
implicit object JObjectYamlFormat extends YamlFormat[JObject] {
override def write(jso: JObject): YamlValue = jso.toYaml
override def read(yv: YamlValue): JObject = yv.convertTo[JValue] match {
case jso: JObject => jso
case x => deserializationError(s"Expected YAML object, got: $x")
}
}
}
import com.tapad.platform.service.model.kubernetes.YamlJsonProtocol._
import com.typesafe.config.{ConfigFactory, ConfigParseOptions, ConfigRenderOptions, ConfigSyntax}
import net.jcazevedo.moultingyaml._
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.scalatest.{FlatSpec, Matchers}
import pureconfig._
class YamlJsonProtocolSpec extends FlatSpec with Matchers {
it should "map json to yaml" in {
val json =
parse("""{ "foo": "bar", "numbers": { "some": [{"number": 1},{"host": "some-host", "port": 8080 }, 3] }}""")
json.toYaml.prettyPrint shouldBe
"""foo: bar
|numbers:
| some:
| - number: 1
| - host: some-host
| port: 8080
| - 3
|""".stripMargin
}
it should "map yaml to json" in {
val yaml = """foo: bar
|numbers:
| some:
| - number: 1
| - host: some-host
| port: 8080
| - 3
|""".stripMargin.parseYaml
compact(yaml.convertTo[JValue]) shouldBe """{"numbers":{"some":[{"number":1},{"host":"some-host","port":8080},3]},"foo":"bar"}"""
}
it should "map case-class -> hocon -> json -> yaml" in {
val hocon =
"""one = foo
|camel-case = 42
""".stripMargin
val caseClass = pureconfig.loadConfigOrThrow[HoconCamelCase](ConfigFactory.parseString(hocon))
val json = parse(ConfigWriter[HoconCamelCase].to(caseClass).render(ConfigRenderOptions.concise()))
val yaml = json.toYaml
yaml.prettyPrint shouldBe
"""camel-case: 42
|one: foo
|""".stripMargin
}
it should "map yaml -> json -> hocon -> case-class" in {
val yaml = """camel-case: 42
|one: foo
|""".stripMargin.parseYaml
val json = compact(yaml.convertTo[JValue])
val jsonSyntax = ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON)
val caseClass = pureconfig.loadConfigOrThrow[HoconCamelCase](ConfigFactory.parseString(json, jsonSyntax))
caseClass.camelCase shouldBe 42
caseClass.one shouldBe "foo"
}
it should "map hocon to json to yaml and preserve camelCase" in {
val hocon =
"""one = foo
|camelCase = 42
""".stripMargin
implicit val camelCase = ProductHint[HoconCamelCase](ConfigFieldMapping(CamelCase, CamelCase))
val caseClass = pureconfig.loadConfigOrThrow[HoconCamelCase](ConfigFactory.parseString(hocon))
val json = parse(ConfigWriter[HoconCamelCase].to(caseClass).render(ConfigRenderOptions.concise()))
val yaml = json.toYaml
yaml.prettyPrint shouldBe
"""camelCase: 42
|one: foo
|""".stripMargin
}
}
case class HoconCamelCase(one: String, camelCase: Int)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment