Class Mappers
Public entry point for the build-time JSON / XML mapping framework.
@Mapped classes get a generated mapper at build time. The generated
mapper's static initializer self-registers with this registry. The
registry stays empty until something triggers each generated class's
<clinit>:
- iOS / Android -- the build server probes the project zip for
cn1app.MapperBootstrap, and when present splices anew cn1app.MapperBootstrap();into the per-build application stub beforeDisplay.init. That constructor references every generated mapper, triggering their static initializers. - JavaSE simulator + desktop --
JavaSEPort#postInitcallsClass.forName("cn1app.MapperBootstrap")so the registry is populated on the same boundary. Classloading is the legitimate path here: JavaSE runs unobfuscated. - Unit tests / manual init -- application code can call
Mappers.register(...)directly to install a hand-written mapper for a class the build can't annotate.
Typical use after init:
String json = Mappers.toJson(user);
User u = Mappers.fromJson(json, User.class);
String xml = Mappers.toXml(user);
User u = Mappers.fromXml(xml, User.class);
The registry is keyed on getClass().getName() so it survives ParparVM
rename and R8 obfuscation: both the registration site and the lookup
site see the same renamed name within a single execution. The map keys
are never persisted, so the renaming has no observable effect on
behavior.
-
Method Summary
Modifier and TypeMethodDescriptionstatic voidappendJson(Object instance, StringBuilder out) Appendsinstanceas a JSON object using its registered mapper, taking theMapper.Directroute when that mapper offers one.static voidappendJsonRaw(StringBuilder out, Object value) Appendsvalueexactly asJSONWriterwould render it if it had been put into the map thatMapper#toMapbuilds.static voidappendJsonString(StringBuilder sb, String s) Appendssas an escaped JSON string, ornull.static voidappendJsonUsing(Mapper<?> mapper, Object instance, StringBuilder out) Appendsinstancethrough the mapper the CALLER names, rather than the one registered for the instance's runtime class.static voidappendJsonValue(StringBuilder out, Object value) Appends any value a generated codec can hold, producing exactly what the map path would.static <T> TParses JSON read from aReader(file, network response, ...) without fully buffering it into a String first.static <T> TInverse of#toJson.static <T> TParses XML read from aReaderwithout fully buffering it first.static <T> TInverse of#toXml.static <T> Mapper<T> Looks up the mapper fortype(bytype.getName()) or null when none is registered.static <T> voidInstallsmapperundermapper.type().getName().static StringSerializesinstanceto JSON.static StringSerializesinstanceto XML.
-
Method Details
-
register
Installsmapperundermapper.type().getName(). The generated per-class mapper's static initializer calls this; hand-written mappers for classes outside the build's annotation scan call it explicitly. -
get
-
toJson
-
fromJson
-
fromJson
-
toXml
-
fromXml
-
fromXml
-
appendJsonValue
Appends any value a generated codec can hold, producing exactly what the map path would.
Public because generated
toJsonmethods call it for the property kinds they cannot render inline -- a nested mapped object, aProperty's value, a list element. A nested object goes through ITS mapper, taking that mapper'sMapper.Directroute when it offers one, so nesting stays free of intermediate maps all the way down.Conversions match
Mapper#toMapexactly, and must keep matching: a date becomes its millisecond value and an enum itsname(), because that is what the map path puts in the map before the writer ever sees it. -
appendJson
Appends
instanceas a JSON object using its registered mapper, taking theMapper.Directroute when that mapper offers one.Unlike
#toJson(Object)this appends rather than returning a String, so nesting does not build one String per level. An unmapped value falls back to itstoString, which is whatMapper#toMapdoes for the same case rather than failing the whole document. -
appendJsonRaw
Appends
valueexactly asJSONWriterwould render it if it had been put into the map thatMapper#toMapbuilds.This is deliberately NOT
#appendJsonValue: that one is smarter, turning aDateinto epoch milliseconds and a mapped object into nested JSON. Where a generated mapper is reproducing what the map path stored RAW -- aPropertyvalue is the case that matters -- being smarter is being different, andMapper.Directpromises identical output rather than better output. -
appendJsonUsing
Appends
instancethrough the mapper the CALLER names, rather than the one registered for the instance's runtime class.The distinction is polymorphism. A field declared
Baseholding an instance of an unmapped subclass finds no mapper by runtime class, and#appendJson(Object, StringBuilder)then falls back to the quotedtoString.Mapper#toMaplooks the mapper up by the DECLARED type and serialises the subclass as an object, so a generated mapper reproducing the map path has to ask the same question. Mirrors what the map path does with a null mapper too: the raw value, which renders as its quotedtoString. -
appendJsonString
Appends
sas an escaped JSON string, ornull.Public because GENERATED mappers call it: a direct writer has to escape exactly the way the map path does, and the only way to guarantee that is for both to use this method rather than each having its own copy.
-