@@ -525,6 +525,7 @@ public class Main {
525525</details >
526526
527527### Decode an object with optional fields.
528+
528529<details >
529530 <summary>Show</summary>
530531
@@ -676,6 +677,53 @@ public class Main {
676677
677678</details >
678679
680+ ### Decode json with a fixed set of keys
681+
682+ <details >
683+ <summary>Show</summary>
684+
685+ ``` java
686+ import dev.mccue.json.Json ;
687+ import dev.mccue.json.JsonDecodeException ;
688+ import dev.mccue.json.JsonDecoder ;
689+
690+ import java.util.HashSet ;
691+ import java.util.Set ;
692+
693+ record Prison(String location) {
694+ public static Prison fromJson(Json json) {
695+ var object = JsonDecoder . object(json);
696+ var expected = Set . of(" location" );
697+ if (! expected. equals(object. keySet())) {
698+ var extra = new HashSet<> (object. keySet());
699+ extra. removeAll(expected);
700+ throw JsonDecodeException . of(" Extra Keys: " + extra, json);
701+ }
702+
703+ return new Prison (
704+ JsonDecoder . field(json, " location" , JsonDecoder :: string)
705+ );
706+ }
707+ }
708+ public class Main {
709+ public static void main (String [] args ) {
710+ Json withExtraKeys = Json . readString(
711+ " " "
712+ {
713+ " location" : " Siberia " ,
714+ " escapeMethod" : " tunnelling"
715+ }
716+ " " "
717+ );
718+
719+ var prison = Prison . fromJson(withExtraKeys);
720+ }
721+ }
722+ ```
723+
724+ </details >
725+
726+
679727### Decode json into bean
680728
681729<details >
@@ -736,7 +784,11 @@ public class Main {
736784 var fozzie = new Fozzie ();
737785 fozzie. setJoke(JsonDecoder . field(json, " joke" , JsonDecoder :: string));
738786 fozzie. setPunchline(JsonDecoder . field(json, " punchline" , JsonDecoder :: string));
739- fozzie. setHecklers(JsonDecoder . field(json, " hecklers" , JsonDecoder . array(JsonDecoder :: string)));
787+ fozzie. setHecklers(JsonDecoder . field(
788+ json,
789+ " hecklers" ,
790+ JsonDecoder . array(JsonDecoder :: string)
791+ ));
740792 return fozzie;
741793 }
742794
@@ -817,4 +869,233 @@ public class Main {
817869[Person[firstName=Great, lastName=Gonzo], Person[firstName=Jim, lastName=Henson]]
818870```
819871
872+ </details >
873+
874+ ### Encode and Decode with Kotlin
875+
876+ <details >
877+ <summary>Show</summary>
878+
879+ ``` kotlin
880+ import dev.mccue.json.Json
881+ import dev.mccue.json.JsonDecoder
882+ import dev.mccue.json.JsonEncodable
883+ import dev.mccue.json.JsonWriteOptions
884+
885+ data class Muppet (
886+ val name : String ,
887+ val scientist : Boolean ,
888+ val lines : String?
889+ ) : JsonEncodable {
890+ override fun toJson (): Json =
891+ Json .objectBuilder()
892+ .put(" name" , name)
893+ .put(" scientist" , scientist)
894+ .put(" lines" , lines)
895+ .build()
896+
897+ companion object {
898+ fun fromJson (json : Json ): Muppet =
899+ Muppet (
900+ JsonDecoder .field(json, " name" ) { JsonDecoder .string(it) },
901+ JsonDecoder .field(json, " scientist" ) { JsonDecoder .boolean_(it) },
902+ JsonDecoder .nullableField(json,
903+ " lines" ,
904+ { JsonDecoder .string(it) },
905+ null
906+ )
907+ )
908+ }
909+ }
910+
911+
912+
913+ data class Movie (
914+ val title : String ,
915+ val muppets : List <Muppet >
916+ ) : JsonEncodable {
917+ override fun toJson (): Json {
918+ return Json .objectBuilder()
919+ .put(" title" , title)
920+ .put(" muppets" , muppets)
921+ .build()
922+ }
923+
924+ companion object {
925+ fun fromJson (json : Json ): Movie =
926+ Movie (
927+ JsonDecoder .field(json, " title" ) { JsonDecoder .string(it) },
928+ JsonDecoder .field(json, " muppets" , JsonDecoder .array { Muppet .fromJson(it) })
929+ )
930+ }
931+ }
932+
933+
934+ fun main (args : Array <String >) {
935+ val movie = Movie (
936+ " Most wanted" ,
937+ listOf (
938+ Muppet (
939+ " kermit" ,
940+ false ,
941+ " I'm not Constantine!"
942+ ),
943+ Muppet (
944+ " beaker" ,
945+ true ,
946+ null
947+ ),
948+ Muppet (
949+ " bunsen" ,
950+ true ,
951+ " I don't mean to be a stickler"
952+ )
953+ )
954+ )
955+
956+ println (Json .writeString(movie, JsonWriteOptions ().withIndentation(4 )))
957+
958+ val movieRoundTripped = Movie .fromJson(Json .readString(Json .writeString(movie)))
959+
960+ println (movieRoundTripped)
961+ println (movie)
962+ println (movie == movieRoundTripped)
963+ }
964+ ```
965+
966+ ```
967+ {
968+ "title": "Most wanted",
969+ "muppets": [
970+ {
971+ "name": "kermit",
972+ "scientist": false,
973+ "lines": "I'm not Constantine!"
974+ },
975+ {
976+ "name": "beaker",
977+ "scientist": true,
978+ "lines": null
979+ },
980+ {
981+ "name": "bunsen",
982+ "scientist": true,
983+ "lines": "I don't mean to be a stickler"
984+ }
985+ ]
986+ }
987+ Movie(title=Most wanted, muppets=[Muppet(name=kermit, scientist=false, lines=I'm not Constantine!), Muppet(name=beaker, scientist=true, lines=null), Muppet(name=bunsen, scientist=true, lines=I don't mean to be a stickler)])
988+ Movie(title=Most wanted, muppets=[Muppet(name=kermit, scientist=false, lines=I'm not Constantine!), Muppet(name=beaker, scientist=true, lines=null), Muppet(name=bunsen, scientist=true, lines=I don't mean to be a stickler)])
989+ true
990+ ```
991+
992+ </details >
993+
994+ ### Encode and Decode with Scala 3
995+
996+ <details >
997+ <summary>Show</summary>
998+
999+ ``` scala
1000+ import dev .mccue .json .{Json , JsonDecoder , JsonEncodable , JsonWriteOptions }
1001+
1002+ import scala .jdk .CollectionConverters ._
1003+
1004+ case class Muppet (name : String , scientist : Boolean , lines : Option [String ]) extends JsonEncodable {
1005+ override def toJson : Json =
1006+ Json .objectBuilder()
1007+ .put(" name" , name)
1008+ .put(" scientist" , scientist)
1009+ .put(" lines" , lines.orNull)
1010+ .build
1011+ }
1012+
1013+ object Muppet {
1014+ def fromJson (json : Json ): Muppet =
1015+ Muppet (
1016+ JsonDecoder .field(json, " name" , JsonDecoder .string _),
1017+ JsonDecoder .field(json, " scientist" , JsonDecoder .boolean_ _),
1018+ JsonDecoder .nullableField(json, " name" , JsonDecoder .string _)
1019+ .map(Option (_))
1020+ .orElse(None )
1021+ )
1022+ }
1023+
1024+ case class Movie (title : String , muppets : Seq [Muppet ]) extends JsonEncodable {
1025+ override def toJson : Json =
1026+ Json .objectBuilder()
1027+ .put(" title" , title)
1028+ .put(" muppets" , muppets.asJava)
1029+ .build()
1030+ }
1031+
1032+ object Movie {
1033+ def fromJson (json : Json ): Movie =
1034+ Movie (
1035+ JsonDecoder .field(json, " title" , JsonDecoder .string _),
1036+ JsonDecoder .field(json, " muppets" , JsonDecoder .array(Muppet .fromJson _))
1037+ .asScala
1038+ .toSeq
1039+ )
1040+ }
1041+
1042+
1043+ @ main
1044+ def main (): Unit = {
1045+ val movie = Movie (
1046+ " Most wanted" ,
1047+ Seq (
1048+ Muppet (
1049+ " kermit" ,
1050+ false ,
1051+ Some (" I'm not Constantine!" )
1052+ ),
1053+ Muppet (
1054+ " beaker" ,
1055+ true ,
1056+ None
1057+ ),
1058+ Muppet (
1059+ " bunsen" ,
1060+ true ,
1061+ Some (" I don't mean to be a stickler" )
1062+ )
1063+ )
1064+ )
1065+
1066+ println(Json .writeString(movie, JsonWriteOptions ().withIndentation(4 )))
1067+
1068+ val movieRoundTripped = Movie .fromJson(Json .readString(Json .writeString(movie)))
1069+
1070+ println(movieRoundTripped)
1071+ println(movie)
1072+ println(movie == movieRoundTripped)
1073+ }
1074+ ```
1075+
1076+ ```
1077+ {
1078+ "title": "Most wanted",
1079+ "muppets": [
1080+ {
1081+ "name": "kermit",
1082+ "scientist": false,
1083+ "lines": "I'm not Constantine!"
1084+ },
1085+ {
1086+ "name": "beaker",
1087+ "scientist": true,
1088+ "lines": null
1089+ },
1090+ {
1091+ "name": "bunsen",
1092+ "scientist": true,
1093+ "lines": "I don't mean to be a stickler"
1094+ }
1095+ ]
1096+ }
1097+ Movie(Most wanted,List(Muppet(kermit,false,Some(kermit)), Muppet(beaker,true,Some(beaker)), Muppet(bunsen,true,Some(bunsen))))
1098+ Movie(Most wanted,List(Muppet(kermit,false,Some(I'm not Constantine!)), Muppet(beaker,true,None), Muppet(bunsen,true,Some(I don't mean to be a stickler))))
1099+ false
1100+ ```
8201101</details >
0 commit comments