Skip to content

Serialize case objects as literal strings #382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Apr 29, 2022
Merged
1 change: 1 addition & 0 deletions implicits/src-3/upickle/implicits/CaseClassReader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ trait CaseClassReaderPiece extends MacrosCommon:
def visitEnd(index: Int): T =
make(builder.toMap)
}
override def visitString(v: CharSequence, index: Int) = make(Map())
end CaseClassReader

inline def macroR[T](using m: Mirror.Of[T]): Reader[T] = inline m match {
Expand Down
5 changes: 4 additions & 1 deletion implicits/src-3/upickle/implicits/CaseClassWriter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ trait CaseClassWriterPiece extends MacrosCommon:

inline def macroW[T: ClassTag](using m: Mirror.Of[T]): Writer[T] = inline m match {
case m: Mirror.ProductOf[T] =>

def elemsInfo(v: T): List[(String, Writer[_], Any)] =
val labels: List[String] = macros.fieldLabels[T]
val writers: List[Writer[_]] =
Expand All @@ -50,8 +51,10 @@ trait CaseClassWriterPiece extends MacrosCommon:
yield (l, w, v)
end elemsInfo
val writer = CaseClassWriter[T](elemsInfo, macros.getDefaultParams[T])
if macros.isSingleton[T] then
annotate(SingletonW[T](null.asInstanceOf[T]), macros.fullClassName[T])

if macros.isMemberOfSealedHierarchy[T] then annotate(writer, macros.fullClassName[T])
else if macros.isMemberOfSealedHierarchy[T] then annotate(writer, macros.fullClassName[T])
else writer
case m: Mirror.SumOf[T] =>
val writers: List[Writer[_ <: T]] = macros.summonList[Tuple.Map[m.MirroredElemTypes, Writer]]
Expand Down
7 changes: 6 additions & 1 deletion implicits/src-3/upickle/implicits/macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def isMemberOfSealedHierarchyImpl[T](using Quotes, Type[T]): Expr[Boolean] =
import quotes.reflect._

val parents = TypeRepr.of[T].baseClasses

Expr(parents.exists { p => p.flags.is(Flags.Sealed) })


Expand All @@ -87,4 +88,8 @@ def fullClassNameImpl[T](using Quotes, Type[T]): Expr[String] =
case Some(name) => Expr(name)
case None => Expr(sym.fullName.replace("$", ""))
}
end fullClassNameImpl

inline def isSingleton[T]: Boolean = ${ isSingletonImpl[T] }
def isSingletonImpl[T](using Quotes, Type[T]): Expr[Boolean] =
import quotes.reflect._
Expr(TypeRepr.of[T].isSingleton)
18 changes: 11 additions & 7 deletions upickle/src/upickle/Api.scala
Original file line number Diff line number Diff line change
Expand Up @@ -301,13 +301,17 @@ trait AttributeTagged extends Api with Annotator{
}
}
def taggedWrite[T, R](w: CaseW[T], tag: String, out: Visitor[_, R], v: T): R = {
val ctx = out.asInstanceOf[Visitor[Any, R]].visitObject(w.length(v) + 1, true, -1)
val keyVisitor = ctx.visitKey(-1)

ctx.visitKeyValue(keyVisitor.visitString(tagName, -1))
ctx.visitValue(ctx.subVisitor.visitString(objectTypeKeyWriteMap(tag), -1), -1)
w.writeToObject(ctx, v)
val res = ctx.visitEnd(-1)
res
if (w.isInstanceOf[SingletonW[_]]) out.visitString(tag, -1)
else {
val ctx = out.asInstanceOf[Visitor[Any, R]].visitObject(w.length(v) + 1, true, -1)
val keyVisitor = ctx.visitKey(-1)

ctx.visitKeyValue(keyVisitor.visitString(tagName, -1))
ctx.visitValue(ctx.subVisitor.visitString(objectTypeKeyWriteMap(tag), -1), -1)
w.writeToObject(ctx, v)
val res = ctx.visitEnd(-1)
res
}
}
}
18 changes: 6 additions & 12 deletions upickle/test/src-2/upickle/AdvancedTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -211,23 +211,17 @@ object AdvancedTests extends TestSuite {
]
}"""
)
rw(
End: LL,
"""{"$type":"upickle.Recursive.End"}""",
""" "upickle.Recursive.End" """
)

rw(
Node(3, End): LL,
rw(End: LL, """ "upickle.Recursive.End" """, """{"$type":"upickle.Recursive.End"}""")
rw(Node(3, End): LL,
"""{
"$type": "upickle.Recursive.Node",
"c": 3,
"next": {"$type":"upickle.Recursive.End"}
"next": "upickle.Recursive.End"
}""",
"""{
"$type": "upickle.Recursive.Node",
"c": 3,
"next": "upickle.Recursive.End"
"next": {"$type":"upickle.Recursive.End"}
}"""
)

Expand All @@ -239,7 +233,7 @@ object AdvancedTests extends TestSuite {
"next": {
"$type": "upickle.Recursive.Node",
"c":3,
"next":{"$type":"upickle.Recursive.End"}
"next": "upickle.Recursive.End"
}
}""",
"""{
Expand All @@ -248,7 +242,7 @@ object AdvancedTests extends TestSuite {
"next": {
"$type": "upickle.Recursive.Node",
"c":3,
"next": "upickle.Recursive.End"
"next":{"$type":"upickle.Recursive.End"}
}
}"""
)
Expand Down
3 changes: 3 additions & 0 deletions upickle/test/src/upickle/ClassDefs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,16 @@ object Singletons{
default.macroRW[CC.type]
)
}

case object BB extends AA
implicit val BBrw: upickle.default.ReadWriter[BB.type] = default.macroRW

case object CC extends AA
implicit val CCrw: upickle.default.ReadWriter[CC.type] = default.macroRW

case object Standalone
}

object Generic{
case class A[T](t: T)

Expand Down
12 changes: 6 additions & 6 deletions upickle/test/src/upickle/MacroTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ object MacroTests extends TestSuite {
test("shallow"){
test - rw(B(1), """{"$type": "upickle.Hierarchy.B", "i":1}""")
test - rw(C("a", "b"), """{"$type": "upickle.Hierarchy.C", "s1":"a","s2":"b"}""")
test - rw(AnZ: Z, """{"$type": "upickle.Hierarchy.AnZ"}""", """ "upickle.Hierarchy.AnZ" """)
test - rw(AnZ, """{"$type": "upickle.Hierarchy.AnZ"}""", """ "upickle.Hierarchy.AnZ" """)
test - rw(AnZ: Z, """ "upickle.Hierarchy.AnZ" """, """{"$type": "upickle.Hierarchy.AnZ"}""")
test - rw(AnZ, """ "upickle.Hierarchy.AnZ" """, """{"$type": "upickle.Hierarchy.AnZ"}""")
test - rw(Hierarchy.B(1): Hierarchy.A, """{"$type": "upickle.Hierarchy.B", "i":1}""")
test - rw(C("a", "b"): A, """{"$type": "upickle.Hierarchy.C", "s1":"a","s2":"b"}""")

Expand Down Expand Up @@ -199,10 +199,10 @@ object MacroTests extends TestSuite {
test("singleton"){
import Singletons._

rw(BB, """{"$type":"upickle.Singletons.BB"}""", """ "upickle.Singletons.BB" """)
rw(CC, """{"$type":"upickle.Singletons.CC"}""", """ "upickle.Singletons.CC" """)
rw(BB: AA, """{"$type":"upickle.Singletons.BB"}""", """ "upickle.Singletons.BB" """)
rw(CC: AA, """{"$type":"upickle.Singletons.CC"}""", """ "upickle.Singletons.CC" """)
rw(BB, """ "upickle.Singletons.BB" """, """{"$type":"upickle.Singletons.BB"}""")
rw(CC, """ "upickle.Singletons.CC" """, """{"$type":"upickle.Singletons.CC"}""")
rw(BB: AA, """ "upickle.Singletons.BB" """, """{"$type":"upickle.Singletons.BB"}""")
rw(CC: AA, """ "upickle.Singletons.CC" """, """{"$type":"upickle.Singletons.CC"}""")
}
}
test("robustnessAgainstVaryingSchemas"){
Expand Down