Skip to content

Commit f3a4e0f

Browse files
author
Yannick Gladow
committed
Fix if json has null and not null for same field
1 parent 120633e commit f3a4e0f

3 files changed

Lines changed: 42 additions & 20 deletions

File tree

src/main/scala/io/yannick_cw/sjq/executeAccessPattern.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import scala.util.Try
1010
object executeAccessPattern {
1111
private def buildCode(access: String, ccs: String) =
1212
s"""
13-
| import io.circe.Json
13+
| import io.circe.{Encoder, Decoder, HCursor, Json}
14+
| implicit val dec: Decoder[Null] = (c: HCursor) => Right(null)
15+
| implicit val enc: Encoder[Null] = (_: Null) => Json.Null
1416
|
1517
| (json: Json) => {
1618
| import io.circe.generic.auto._

src/main/scala/io/yannick_cw/sjq/jsonToCaseClass.scala

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package io.yannick_cw.sjq
22

3+
import cats.instances.map.catsKernelStdMonoidForMap
4+
import cats.instances.int.catsKernelStdGroupForInt
5+
import cats.syntax.foldable._
6+
import cats.instances.list._
7+
import cats.kernel.Semigroup
38
import io.circe.Json
4-
import cats.implicits._
59

610
object jsonToCaseClass {
711
case class CC(name: String, content: Map[String, String])
@@ -17,13 +21,12 @@ object jsonToCaseClass {
1721
private def renderCC(cc: CC): String =
1822
s"case class ${cc.name}(${cc.content.map { case (key, value) => s"$key: $value" }.mkString(", ")})"
1923

20-
private def buildCC(j: Json, nextLevelName: String, doneCCs: List[CC]): (String, List[CC]) = {
24+
private def buildCC(j: Json, nextLevelName: String, doneCCs: List[CC]): (Option[String], List[CC]) = {
2125
j.fold(
22-
// todo decode not at all if null
23-
"Option[String]" -> doneCCs,
24-
_ => "Boolean" -> doneCCs,
25-
_ => "Double" -> doneCCs,
26-
_ => "String" -> doneCCs,
26+
Some("Null") -> doneCCs,
27+
_ => Some("Boolean") -> doneCCs,
28+
_ => Some("Double") -> doneCCs,
29+
_ => Some("String") -> doneCCs,
2730
array =>
2831
array.toList match {
2932
case all @ ele :: rest if all.forall(_.isObject) =>
@@ -32,15 +35,16 @@ object jsonToCaseClass {
3235
case ((newValueName, aggCCs), nextJson) =>
3336
newValueName -> buildCC(nextJson, nextLevelName, aggCCs)._2
3437
}
35-
3638
val (valueName, ccs) = innerType match {
37-
case (valueName, Nil) => valueName -> doneCCs
38-
case (valueName, ele :: Nil) => valueName -> (ele :: Nil)
39-
case (valueName, ele :: more) =>
39+
case (Some(valueName), Nil) => valueName -> doneCCs
40+
case (Some(valueName), ele :: Nil) => valueName -> (ele :: Nil)
41+
case (Some(valueName), ele :: more) =>
4042
val (nextLevelCCs, otherCCs) = (ele :: more)
4143
.partition(_.name.startsWith(valueName))
4244

43-
val allContent = nextLevelCCs.map(_.content).fold(Map.empty)(_ ++ _)
45+
implicit val sSemi: Semigroup[String] =
46+
(x: String, y: String) => if (x == "Null") s"Option[$y]" else if (y == "Null") s"Option[$x]" else x
47+
val allContent = nextLevelCCs.map(_.content).combineAll
4448
val keyCountPerContent =
4549
nextLevelCCs.map(cc => cc.content.map[String, Int] { case (key, _) => (key, 1) }).combineAll
4650

@@ -51,22 +55,23 @@ object jsonToCaseClass {
5155
case (key, _) => key -> s"Option[${allContent(key)}]"
5256
}))
5357
valueName -> (newCC :: otherCCs)
58+
case _ => "" -> doneCCs
5459
}
5560

56-
s"List[$valueName]" -> ccs
61+
Some(s"List[$valueName]") -> ccs
5762
case ele :: rest if rest.forall(_ == ele) =>
5863
val (newValue, allCCs) = buildCC(ele, nextLevelName, doneCCs)
59-
s"List[$newValue]" -> allCCs
60-
case _ => "List[String]" -> doneCCs
64+
newValue.map(v => s"List[$v]") -> allCCs
65+
case _ => Some("List[String]") -> doneCCs
6166
},
6267
jObj => {
6368
val (allNewCCs, newCC) = jObj.toMap.foldLeft(doneCCs -> CC(nextLevelName, Map.empty)) {
6469
case ((allCCs, currCC), (key, value)) =>
6570
val safeNextLevelName = findFreeName(currCC :: allCCs, key)
6671
val (newValue, allNewCCs) = buildCC(value, safeNextLevelName, allCCs)
67-
(allNewCCs, currCC.copy(content = currCC.content.updated(key, newValue)))
72+
allNewCCs -> newValue.map(v => currCC.copy(content = currCC.content.updated(key, v))).getOrElse(currCC)
6873
}
69-
(nextLevelName, newCC :: allNewCCs)
74+
(Some(nextLevelName), newCC :: allNewCCs)
7075
}
7176
)
7277
}

src/test/scala/io/yannick_cw/sjq/jsonToCaseClassTest.scala

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,26 @@ class jsonToCaseClassTest extends FlatSpec with Matchers {
5151
it should "parse json with null values" in {
5252
val json = parse("""
5353
| {
54-
| "name": null
54+
| "name": null,
55+
| "id": 22
5556
| }
5657
""".stripMargin).fold(throw _, x => x)
5758

58-
jsonToCaseClass(json) shouldBe List(("CC", "case class CC(name: Option[String])"))
59+
jsonToCaseClass(json) shouldBe List(("CC", "case class CC(name: Null, id: Double)"))
60+
}
61+
62+
it should "parse json with null values and not null values for a field" in {
63+
val json = parse("""
64+
| {
65+
| "things": [
66+
| { "id": null },
67+
| { "id": 22 }
68+
| ]
69+
| }
70+
""".stripMargin).fold(throw _, x => x)
71+
72+
jsonToCaseClass(json) shouldBe List(("CC", "case class CC(things: List[things])"),
73+
("things", "case class things(id: Option[Double])"))
5974
}
6075

6176
it should "parse json empty lists" in {

0 commit comments

Comments
 (0)