1Moshi 2===== 3 4Moshi is a modern JSON library for Android, Java and Kotlin. It makes it easy to parse JSON into Java and Kotlin 5classes: 6 7_Note: The Kotlin examples of this README assume use of either Kotlin code gen or `KotlinJsonAdapterFactory` for reflection. Plain Java-based reflection is unsupported on Kotlin classes._ 8 9<details open> 10 <summary>Java</summary> 11 12```java 13String json = ...; 14 15Moshi moshi = new Moshi.Builder().build(); 16JsonAdapter<BlackjackHand> jsonAdapter = moshi.adapter(BlackjackHand.class); 17 18BlackjackHand blackjackHand = jsonAdapter.fromJson(json); 19System.out.println(blackjackHand); 20``` 21</details> 22 23<details> 24 <summary>Kotlin</summary> 25 26```kotlin 27val json: String = ... 28 29val moshi: Moshi = Moshi.Builder().build() 30val jsonAdapter: JsonAdapter<BlackjackHand> = moshi.adapter<BlackjackHand>() 31 32val blackjackHand = jsonAdapter.fromJson(json) 33println(blackjackHand) 34``` 35</details> 36 37And it can just as easily serialize Java or Kotlin objects as JSON: 38 39<details open> 40 <summary>Java</summary> 41 42```java 43BlackjackHand blackjackHand = new BlackjackHand( 44 new Card('6', SPADES), 45 Arrays.asList(new Card('4', CLUBS), new Card('A', HEARTS))); 46 47Moshi moshi = new Moshi.Builder().build(); 48JsonAdapter<BlackjackHand> jsonAdapter = moshi.adapter(BlackjackHand.class); 49 50String json = jsonAdapter.toJson(blackjackHand); 51System.out.println(json); 52``` 53</details> 54 55<details> 56 <summary>Kotlin</summary> 57 58```kotlin 59val blackjackHand = BlackjackHand( 60 Card('6', SPADES), 61 listOf(Card('4', CLUBS), Card('A', HEARTS)) 62 ) 63 64val moshi: Moshi = Moshi.Builder().build() 65val jsonAdapter: JsonAdapter<BlackjackHand> = moshi.adapter<BlackjackHand>() 66 67val json: String = jsonAdapter.toJson(blackjackHand) 68println(json) 69``` 70</details> 71 72### Built-in Type Adapters 73 74Moshi has built-in support for reading and writing Java’s core data types: 75 76 * Primitives (int, float, char...) and their boxed counterparts (Integer, Float, Character...). 77 * Arrays, Collections, Lists, Sets, and Maps 78 * Strings 79 * Enums 80 81It supports your model classes by writing them out field-by-field. In the example above Moshi uses 82these classes: 83 84<details open> 85 <summary>Java</summary> 86 87```java 88class BlackjackHand { 89 public final Card hidden_card; 90 public final List<Card> visible_cards; 91 ... 92} 93 94class Card { 95 public final char rank; 96 public final Suit suit; 97 ... 98} 99 100enum Suit { 101 CLUBS, DIAMONDS, HEARTS, SPADES; 102} 103``` 104</details> 105 106<details> 107 <summary>Kotlin</summary> 108 109```kotlin 110class BlackjackHand( 111 val hidden_card: Card, 112 val visible_cards: List<Card>, 113 ... 114) 115 116class Card( 117 val rank: Char, 118 val suit: Suit 119 ... 120) 121 122enum class Suit { 123 CLUBS, DIAMONDS, HEARTS, SPADES; 124} 125``` 126</details> 127 128 129to read and write this JSON: 130 131```json 132{ 133 "hidden_card": { 134 "rank": "6", 135 "suit": "SPADES" 136 }, 137 "visible_cards": [ 138 { 139 "rank": "4", 140 "suit": "CLUBS" 141 }, 142 { 143 "rank": "A", 144 "suit": "HEARTS" 145 } 146 ] 147} 148``` 149 150The [Javadoc][javadoc] catalogs the complete Moshi API, which we explore below. 151 152### Custom Type Adapters 153 154With Moshi, it’s particularly easy to customize how values are converted to and from JSON. A type 155adapter is any class that has methods annotated `@ToJson` and `@FromJson`. 156 157For example, Moshi’s default encoding of a playing card is verbose: the JSON defines the rank and 158suit in separate fields: `{"rank":"A","suit":"HEARTS"}`. With a type adapter, we can change the 159encoding to something more compact: `"4H"` for the four of hearts or `"JD"` for the jack of 160diamonds: 161 162<details open> 163 <summary>Java</summary> 164 165```java 166class CardAdapter { 167 @ToJson String toJson(Card card) { 168 return card.rank + card.suit.name().substring(0, 1); 169 } 170 171 @FromJson Card fromJson(String card) { 172 if (card.length() != 2) throw new JsonDataException("Unknown card: " + card); 173 174 char rank = card.charAt(0); 175 switch (card.charAt(1)) { 176 case 'C': return new Card(rank, Suit.CLUBS); 177 case 'D': return new Card(rank, Suit.DIAMONDS); 178 case 'H': return new Card(rank, Suit.HEARTS); 179 case 'S': return new Card(rank, Suit.SPADES); 180 default: throw new JsonDataException("unknown suit: " + card); 181 } 182 } 183} 184``` 185</details> 186 187<details> 188 <summary>Kotlin</summary> 189 190```kotlin 191class CardAdapter { 192 @ToJson fun toJson(card: Card): String { 193 return card.rank + card.suit.name.substring(0, 1) 194 } 195 196 @FromJson fun fromJson(card: String): Card { 197 if (card.length != 2) throw JsonDataException("Unknown card: $card") 198 199 val rank = card[0] 200 return when (card[1]) { 201 'C' -> Card(rank, Suit.CLUBS) 202 'D' -> Card(rank, Suit.DIAMONDS) 203 'H' -> Card(rank, Suit.HEARTS) 204 'S' -> Card(rank, Suit.SPADES) 205 else -> throw JsonDataException("unknown suit: $card") 206 } 207 } 208} 209``` 210</details> 211 212Register the type adapter with the `Moshi.Builder` and we’re good to go. 213 214<details open> 215 <summary>Java</summary> 216 217```java 218Moshi moshi = new Moshi.Builder() 219 .add(new CardAdapter()) 220 .build(); 221``` 222</details> 223 224<details> 225 <summary>Kotlin</summary> 226 227```kotlin 228val moshi = Moshi.Builder() 229 .add(CardAdapter()) 230 .build() 231``` 232</details> 233 234Voilà: 235 236```json 237{ 238 "hidden_card": "6S", 239 "visible_cards": [ 240 "4C", 241 "AH" 242 ] 243} 244``` 245 246#### Another example 247 248Note that the method annotated with `@FromJson` does not need to take a String as an argument. 249Rather it can take input of any type and Moshi will first parse the JSON to an object of that type 250and then use the `@FromJson` method to produce the desired final value. Conversely, the method 251annotated with `@ToJson` does not have to produce a String. 252 253Assume, for example, that we have to parse a JSON in which the date and time of an event are 254represented as two separate strings. 255 256```json 257{ 258 "title": "Blackjack tournament", 259 "begin_date": "20151010", 260 "begin_time": "17:04" 261} 262``` 263 264We would like to combine these two fields into one string to facilitate the date parsing at a 265later point. Also, we would like to have all variable names in CamelCase. Therefore, the `Event` 266class we want Moshi to produce like this: 267 268<details open> 269 <summary>Java</summary> 270 271```java 272class Event { 273 String title; 274 String beginDateAndTime; 275} 276``` 277</details> 278 279<details> 280 <summary>Kotlin</summary> 281 282```kotlin 283class Event( 284 val title: String, 285 val beginDateAndTime: String 286) 287``` 288</details> 289 290Instead of manually parsing the JSON line per line (which we could also do) we can have Moshi do the 291transformation automatically. We simply define another class `EventJson` that directly corresponds 292to the JSON structure: 293 294<details open> 295 <summary>Java</summary> 296 297```java 298class EventJson { 299 String title; 300 String begin_date; 301 String begin_time; 302} 303``` 304</details> 305 306<details> 307 <summary>Kotlin</summary> 308 309```kotlin 310class EventJson( 311 val title: String, 312 val begin_date: String, 313 val begin_time: String 314) 315``` 316</details> 317 318And another class with the appropriate `@FromJson` and `@ToJson` methods that are telling Moshi how 319to convert an `EventJson` to an `Event` and back. Now, whenever we are asking Moshi to parse a JSON 320to an `Event` it will first parse it to an `EventJson` as an intermediate step. Conversely, to 321serialize an `Event` Moshi will first create an `EventJson` object and then serialize that object as 322usual. 323 324<details open> 325 <summary>Java</summary> 326 327```java 328class EventJsonAdapter { 329 @FromJson Event eventFromJson(EventJson eventJson) { 330 Event event = new Event(); 331 event.title = eventJson.title; 332 event.beginDateAndTime = eventJson.begin_date + " " + eventJson.begin_time; 333 return event; 334 } 335 336 @ToJson EventJson eventToJson(Event event) { 337 EventJson json = new EventJson(); 338 json.title = event.title; 339 json.begin_date = event.beginDateAndTime.substring(0, 8); 340 json.begin_time = event.beginDateAndTime.substring(9, 14); 341 return json; 342 } 343} 344``` 345</details> 346 347<details> 348 <summary>Kotlin</summary> 349 350```kotlin 351class EventJsonAdapter { 352 @FromJson fun eventFromJson(eventJson: EventJson): Event { 353 val event = Event() 354 event.title = eventJson.title 355 event.beginDateAndTime = "${eventJson.begin_date} ${eventJson.begin_time}" 356 return event 357 } 358 359 @ToJson fun eventToJson(event: Event): EventJson { 360 val json = EventJson() 361 json.title = event.title 362 json.begin_date = event.beginDateAndTime.substring(0, 8) 363 json.begin_time = event.beginDateAndTime.substring(9, 14) 364 return json 365 } 366} 367``` 368</details> 369 370Again we register the adapter with Moshi. 371 372<details open> 373 <summary>Java</summary> 374 375```java 376Moshi moshi = new Moshi.Builder() 377 .add(new EventJsonAdapter()) 378 .build(); 379``` 380</details> 381 382<details> 383 <summary>Kotlin</summary> 384 385```kotlin 386val moshi = Moshi.Builder() 387 .add(EventJsonAdapter()) 388 .builder 389``` 390</details> 391 392We can now use Moshi to parse the JSON directly to an `Event`. 393 394<details open> 395 <summary>Java</summary> 396 397```java 398JsonAdapter<Event> jsonAdapter = moshi.adapter(Event.class); 399Event event = jsonAdapter.fromJson(json); 400``` 401</details> 402 403<details> 404 <summary>Kotlin</summary> 405 406```kotlin 407val jsonAdapter = moshi.adapter<Event>() 408val event = jsonAdapter.fromJson(json) 409``` 410</details> 411 412### Adapter convenience methods 413 414Moshi provides a number of convenience methods for `JsonAdapter` objects: 415- `nullSafe()` 416- `nonNull()` 417- `lenient()` 418- `failOnUnknown()` 419- `indent()` 420- `serializeNulls()` 421 422These factory methods wrap an existing `JsonAdapter` into additional functionality. 423For example, if you have an adapter that doesn't support nullable values, you can use `nullSafe()` to make it null safe: 424 425<details open> 426 <summary>Java</summary> 427 428```java 429String dateJson = "\"2018-11-26T11:04:19.342668Z\""; 430String nullDateJson = "null"; 431 432// Hypothetical IsoDateDapter, doesn't support null by default 433JsonAdapter<Date> adapter = new IsoDateDapter(); 434 435Date date = adapter.fromJson(dateJson); 436System.out.println(date); // Mon Nov 26 12:04:19 CET 2018 437 438Date nullDate = adapter.fromJson(nullDateJson); 439// Exception, com.squareup.moshi.JsonDataException: Expected a string but was NULL at path $ 440 441Date nullDate = adapter.nullSafe().fromJson(nullDateJson); 442System.out.println(nullDate); // null 443``` 444</details> 445 446<details> 447 <summary>Kotlin</summary> 448 449```kotlin 450val dateJson = "\"2018-11-26T11:04:19.342668Z\"" 451val nullDateJson = "null" 452 453// Hypothetical IsoDateDapter, doesn't support null by default 454val adapter: JsonAdapter<Date> = IsoDateDapter() 455 456val date = adapter.fromJson(dateJson) 457println(date) // Mon Nov 26 12:04:19 CET 2018 458 459val nullDate = adapter.fromJson(nullDateJson) 460// Exception, com.squareup.moshi.JsonDataException: Expected a string but was NULL at path $ 461 462val nullDate = adapter.nullSafe().fromJson(nullDateJson) 463println(nullDate) // null 464``` 465</details> 466 467In contrast to `nullSafe()` there is `nonNull()` to make an adapter refuse null values. Refer to the Moshi JavaDoc for details on the various methods. 468 469### Parse JSON Arrays 470 471Say we have a JSON string of this structure: 472 473```json 474[ 475 { 476 "rank": "4", 477 "suit": "CLUBS" 478 }, 479 { 480 "rank": "A", 481 "suit": "HEARTS" 482 } 483] 484``` 485 486We can now use Moshi to parse the JSON string into a `List<Card>`. 487 488<details open> 489 <summary>Java</summary> 490 491```java 492String cardsJsonResponse = ...; 493Type type = Types.newParameterizedType(List.class, Card.class); 494JsonAdapter<List<Card>> adapter = moshi.adapter(type); 495List<Card> cards = adapter.fromJson(cardsJsonResponse); 496``` 497</details> 498 499<details> 500 <summary>Kotlin</summary> 501 502```kotlin 503val cardsJsonResponse: String = ... 504// We can just use a reified extension! 505val adapter = moshi.adapter<List<Card>>() 506val cards: List<Card> = adapter.fromJson(cardsJsonResponse) 507``` 508</details> 509 510### Fails Gracefully 511 512Automatic databinding almost feels like magic. But unlike the black magic that typically accompanies 513reflection, Moshi is designed to help you out when things go wrong. 514 515``` 516JsonDataException: Expected one of [CLUBS, DIAMONDS, HEARTS, SPADES] but was ANCHOR at path $.visible_cards[2].suit 517 at com.squareup.moshi.JsonAdapters$11.fromJson(JsonAdapters.java:188) 518 at com.squareup.moshi.JsonAdapters$11.fromJson(JsonAdapters.java:180) 519 ... 520``` 521 522Moshi always throws a standard `java.io.IOException` if there is an error reading the JSON document, 523or if it is malformed. It throws a `JsonDataException` if the JSON document is well-formed, but 524doesn’t match the expected format. 525 526### Built on Okio 527 528Moshi uses [Okio][okio] for simple and powerful I/O. It’s a fine complement to [OkHttp][okhttp], 529which can share buffer segments for maximum efficiency. 530 531### Borrows from Gson 532 533Moshi uses the same streaming and binding mechanisms as [Gson][gson]. If you’re a Gson user you’ll 534find Moshi works similarly. If you try Moshi and don’t love it, you can even migrate to Gson without 535much violence! 536 537But the two libraries have a few important differences: 538 539 * **Moshi has fewer built-in type adapters.** For example, you need to configure your own date 540 adapter. Most binding libraries will encode whatever you throw at them. Moshi refuses to 541 serialize platform types (`java.*`, `javax.*`, and `android.*`) without a user-provided type 542 adapter. This is intended to prevent you from accidentally locking yourself to a specific JDK or 543 Android release. 544 * **Moshi is less configurable.** There’s no field naming strategy, versioning, instance creators, 545 or long serialization policy. Instead of naming a field `visibleCards` and using a policy class 546 to convert that to `visible_cards`, Moshi wants you to just name the field `visible_cards` as it 547 appears in the JSON. 548 * **Moshi doesn’t have a `JsonElement` model.** Instead it just uses built-in types like `List` and 549 `Map`. 550 * **No HTML-safe escaping.** Gson encodes `=` as `\u003d` by default so that it can be safely 551 encoded in HTML without additional escaping. Moshi encodes it naturally (as `=`) and assumes that 552 the HTML encoder – if there is one – will do its job. 553 554### Custom field names with @Json 555 556Moshi works best when your JSON objects and Java or Kotlin classes have the same structure. But when they 557don't, Moshi has annotations to customize data binding. 558 559Use `@Json` to specify how Java fields or Kotlin properties map to JSON names. This is necessary when the JSON name 560contains spaces or other characters that aren’t permitted in Java field or Kotlin property names. For example, this 561JSON has a field name containing a space: 562 563```json 564{ 565 "username": "jesse", 566 "lucky number": 32 567} 568``` 569 570With `@Json` its corresponding Java or Kotlin class is easy: 571 572<details open> 573 <summary>Java</summary> 574 575```java 576class Player { 577 String username; 578 @Json(name = "lucky number") int luckyNumber; 579 580 ... 581} 582``` 583</details> 584 585<details> 586 <summary>Kotlin</summary> 587 588```kotlin 589class Player { 590 val username: String 591 @Json(name = "lucky number") val luckyNumber: Int 592 593 ... 594} 595``` 596</details> 597 598Because JSON field names are always defined with their Java or Kotlin fields, Moshi makes it easy to find 599fields when navigating between Java or Koltin and JSON. 600 601### Alternate type adapters with @JsonQualifier 602 603Use `@JsonQualifier` to customize how a type is encoded for some fields without changing its 604encoding everywhere. This works similarly to the qualifier annotations in dependency injection 605tools like Dagger and Guice. 606 607Here’s a JSON message with two integers and a color: 608 609```json 610{ 611 "width": 1024, 612 "height": 768, 613 "color": "#ff0000" 614} 615``` 616 617By convention, Android programs also use `int` for colors: 618 619<details open> 620 <summary>Java</summary> 621 622```java 623class Rectangle { 624 int width; 625 int height; 626 int color; 627} 628``` 629</details> 630 631<details> 632 <summary>Kotlin</summary> 633 634```kotlin 635class Rectangle( 636 val width: Int, 637 val height: Int, 638 val color: Int 639) 640``` 641</details> 642 643But if we encoded the above Java or Kotlin class as JSON, the color isn't encoded properly! 644 645```json 646{ 647 "width": 1024, 648 "height": 768, 649 "color": 16711680 650} 651``` 652 653The fix is to define a qualifier annotation, itself annotated `@JsonQualifier`: 654 655<details open> 656 <summary>Java</summary> 657 658```java 659@Retention(RUNTIME) 660@JsonQualifier 661public @interface HexColor { 662} 663``` 664</details> 665 666<details> 667 <summary>Kotlin</summary> 668 669```kotlin 670@Retention(RUNTIME) 671@JsonQualifier 672annotation class HexColor 673``` 674</details> 675 676 677Next apply this `@HexColor` annotation to the appropriate field: 678 679<details open> 680 <summary>Java</summary> 681 682```java 683class Rectangle { 684 int width; 685 int height; 686 @HexColor int color; 687} 688``` 689</details> 690 691<details> 692 <summary>Kotlin</summary> 693 694```kotlin 695class Rectangle( 696 val width: Int, 697 val height: Int, 698 @HexColor val color: Int 699) 700``` 701</details> 702 703And finally define a type adapter to handle it: 704 705<details open> 706 <summary>Java</summary> 707 708```java 709/** Converts strings like #ff0000 to the corresponding color ints. */ 710class ColorAdapter { 711 @ToJson String toJson(@HexColor int rgb) { 712 return String.format("#%06x", rgb); 713 } 714 715 @FromJson @HexColor int fromJson(String rgb) { 716 return Integer.parseInt(rgb.substring(1), 16); 717 } 718} 719``` 720</details> 721 722<details> 723 <summary>Kotlin</summary> 724 725```kotlin 726/** Converts strings like #ff0000 to the corresponding color ints. */ 727class ColorAdapter { 728 @ToJson fun toJson(@HexColor rgb: Int): String { 729 return "#%06x".format(rgb) 730 } 731 732 @FromJson @HexColor fun fromJson(rgb: String): Int { 733 return rgb.substring(1).toInt(16) 734 } 735} 736``` 737</details> 738 739Use `@JsonQualifier` when you need different JSON encodings for the same type. Most programs 740shouldn’t need this `@JsonQualifier`, but it’s very handy for those that do. 741 742### Omit fields with `transient` 743 744Some models declare fields that shouldn’t be included in JSON. For example, suppose our blackjack 745hand has a `total` field with the sum of the cards: 746 747<details open> 748 <summary>Java</summary> 749 750```java 751public final class BlackjackHand { 752 private int total; 753 754 ... 755} 756``` 757</details> 758 759<details> 760 <summary>Kotlin</summary> 761 762```kotlin 763class BlackjackHand( 764 private val total: Int, 765 766 ... 767) 768``` 769</details> 770 771By default, all fields are emitted when encoding JSON, and all fields are accepted when decoding 772JSON. Prevent a field from being included by adding Java’s `transient` keyword or Kotlin's `@Transient` annotation: 773 774<details open> 775 <summary>Java</summary> 776 777```java 778public final class BlackjackHand { 779 private transient int total; 780 781 ... 782} 783``` 784</details> 785 786<details> 787 <summary>Kotlin</summary> 788 789```kotlin 790class BlackjackHand(...) { 791 @Transient var total: Int 792 793 ... 794} 795``` 796</details> 797 798Transient fields are omitted when writing JSON. When reading JSON, the field is skipped even if the 799JSON contains a value for the field. Instead, it will get a default value. 800 801 802### Default Values & Constructors 803 804When reading JSON that is missing a field, Moshi relies on the Java or Kotlin or Android runtime to assign 805the field’s value. Which value it uses depends on whether the class has a no-arguments constructor. 806 807If the class has a no-arguments constructor, Moshi will call that constructor and whatever value 808it assigns will be used. For example, because this class has a no-arguments constructor the `total` 809field is initialized to `-1`. 810 811Note: This section only applies to Java reflections. 812 813```java 814public final class BlackjackHand { 815 private int total = -1; 816 ... 817 818 private BlackjackHand() { 819 } 820 821 public BlackjackHand(Card hidden_card, List<Card> visible_cards) { 822 ... 823 } 824} 825``` 826 827If the class doesn’t have a no-arguments constructor, Moshi can’t assign the field’s default value, 828**even if it’s specified in the field declaration**. Instead, the field’s default is always `0` for 829numbers, `false` for booleans, and `null` for references. In this example, the default value of 830`total` is `0`! 831 832 833```java 834public final class BlackjackHand { 835 private int total = -1; 836 ... 837 838 public BlackjackHand(Card hidden_card, List<Card> visible_cards) { 839 ... 840 } 841} 842``` 843 844This is surprising and is a potential source of bugs! For this reason consider defining a 845no-arguments constructor in classes that you use with Moshi, using `@SuppressWarnings("unused")` to 846prevent it from being inadvertently deleted later: 847 848 849```java 850public final class BlackjackHand { 851 private int total = -1; 852 ... 853 854 @SuppressWarnings("unused") // Moshi uses this! 855 private BlackjackHand() { 856 } 857 858 public BlackjackHand(Card hidden_card, List<Card> visible_cards) { 859 ... 860 } 861} 862``` 863 864### Composing Adapters 865 866In some situations Moshi's default Java-to-JSON conversion isn't sufficient. You can compose 867adapters to build upon the standard conversion. 868 869In this example, we turn serialize nulls, then delegate to the built-in adapter: 870 871<details open> 872 <summary>Java</summary> 873 874```java 875class TournamentWithNullsAdapter { 876 @ToJson void toJson(JsonWriter writer, Tournament tournament, 877 JsonAdapter<Tournament> delegate) throws IOException { 878 boolean wasSerializeNulls = writer.getSerializeNulls(); 879 writer.setSerializeNulls(true); 880 try { 881 delegate.toJson(writer, tournament); 882 } finally { 883 writer.setLenient(wasSerializeNulls); 884 } 885 } 886} 887``` 888</details> 889 890<details> 891 <summary>Kotlin</summary> 892 893```kotlin 894class TournamentWithNullsAdapter { 895 @ToJson fun toJson(writer: JsonWriter, tournament: Tournament?, 896 delegate: JsonAdapter<Tournament?>) { 897 val wasSerializeNulls: Boolean = writer.getSerializeNulls() 898 writer.setSerializeNulls(true) 899 try { 900 delegate.toJson(writer, tournament) 901 } finally { 902 writer.setLenient(wasSerializeNulls) 903 } 904 } 905} 906``` 907</details> 908 909 910When we use this to serialize a tournament, nulls are written! But nulls elsewhere in our JSON 911document are skipped as usual. 912 913Moshi has a powerful composition system in its `JsonAdapter.Factory` interface. We can hook in to 914the encoding and decoding process for any type, even without knowing about the types beforehand. In 915this example, we customize types annotated `@AlwaysSerializeNulls`, which an annotation we create, 916not built-in to Moshi: 917 918<details open> 919 <summary>Java</summary> 920 921```java 922@Target(TYPE) 923@Retention(RUNTIME) 924public @interface AlwaysSerializeNulls {} 925``` 926</details> 927 928<details> 929 <summary>Kotlin</summary> 930 931```kotlin 932@Target(TYPE) 933@Retention(RUNTIME) 934annotation class AlwaysSerializeNulls 935``` 936</details> 937 938<details open> 939 <summary>Java</summary> 940 941```java 942@AlwaysSerializeNulls 943static class Car { 944 String make; 945 String model; 946 String color; 947} 948``` 949</details> 950 951<details> 952 <summary>Kotlin</summary> 953 954```kotlin 955@AlwaysSerializeNulls 956class Car( 957 val make: String?, 958 val model: String?, 959 val color: String? 960) 961``` 962</details> 963 964Each `JsonAdapter.Factory` interface is invoked by `Moshi` when it needs to build an adapter for a 965user's type. The factory either returns an adapter to use, or null if it doesn't apply to the 966requested type. In our case we match all classes that have our annotation. 967 968<details open> 969 <summary>Java</summary> 970 971```java 972static class AlwaysSerializeNullsFactory implements JsonAdapter.Factory { 973 @Override public JsonAdapter<?> create( 974 Type type, Set<? extends Annotation> annotations, Moshi moshi) { 975 Class<?> rawType = Types.getRawType(type); 976 if (!rawType.isAnnotationPresent(AlwaysSerializeNulls.class)) { 977 return null; 978 } 979 980 JsonAdapter<Object> delegate = moshi.nextAdapter(this, type, annotations); 981 return delegate.serializeNulls(); 982 } 983} 984``` 985</details> 986 987<details> 988 <summary>Kotlin</summary> 989 990```kotlin 991class AlwaysSerializeNullsFactory : JsonAdapter.Factory { 992 override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? { 993 val rawType: Class<*> = type.rawType 994 if (!rawType.isAnnotationPresent(AlwaysSerializeNulls::class.java)) { 995 return null 996 } 997 val delegate: JsonAdapter<Any> = moshi.nextAdapter(this, type, annotations) 998 return delegate.serializeNulls() 999 } 1000} 1001``` 1002</details> 1003 1004After determining that it applies, the factory looks up Moshi's built-in adapter by calling 1005`Moshi.nextAdapter()`. This is key to the composition mechanism: adapters delegate to each other! 1006The composition in this example is simple: it applies the `serializeNulls()` transform on the 1007delegate. 1008 1009Composing adapters can be very sophisticated: 1010 1011 * An adapter could transform the input object before it is JSON-encoded. A string could be 1012 trimmed or truncated; a value object could be simplified or normalized. 1013 1014 * An adapter could repair the output object after it is JSON-decoded. It could fill-in missing 1015 data or discard unwanted data. 1016 1017 * The JSON could be given extra structure, such as wrapping values in objects or arrays. 1018 1019Moshi is itself built on the pattern of repeatedly composing adapters. For example, Moshi's built-in 1020adapter for `List<T>` delegates to the adapter of `T`, and calls it repeatedly. 1021 1022### Precedence 1023 1024Moshi's composition mechanism tries to find the best adapter for each type. It starts with the first 1025adapter or factory registered with `Moshi.Builder.add()`, and proceeds until it finds an adapter for 1026the target type. 1027 1028If a type can be matched multiple adapters, the earliest one wins. 1029 1030To register an adapter at the end of the list, use `Moshi.Builder.addLast()` instead. This is most 1031useful when registering general-purpose adapters, such as the `KotlinJsonAdapterFactory` below. 1032 1033Kotlin 1034------ 1035 1036Moshi is a great JSON library for Kotlin. It understands Kotlin’s non-nullable types and default 1037parameter values. When you use Kotlin with Moshi you may use reflection, codegen, or both. 1038 1039#### Reflection 1040 1041The reflection adapter uses Kotlin’s reflection library to convert your Kotlin classes to and from 1042JSON. Enable it by adding the `KotlinJsonAdapterFactory` to your `Moshi.Builder`: 1043 1044```kotlin 1045val moshi = Moshi.Builder() 1046 .addLast(KotlinJsonAdapterFactory()) 1047 .build() 1048``` 1049 1050Moshi’s adapters are ordered by precedence, so you should use `addLast()` with 1051`KotlinJsonAdapterFactory`, and `add()` with your custom adapters. 1052 1053The reflection adapter requires the following additional dependency: 1054 1055```xml 1056<dependency> 1057 <groupId>com.squareup.moshi</groupId> 1058 <artifactId>moshi-kotlin</artifactId> 1059 <version>1.12.0</version> 1060</dependency> 1061``` 1062 1063```kotlin 1064implementation("com.squareup.moshi:moshi-kotlin:1.13.0") 1065``` 1066 1067Note that the reflection adapter transitively depends on the `kotlin-reflect` library which is a 10682.5 MiB .jar file. 1069 1070#### Codegen 1071 1072Moshi’s Kotlin codegen support is an annotation processor. It generates a small and fast adapter for 1073each of your Kotlin classes at compile time. Enable it by annotating each class that you want to 1074encode as JSON: 1075 1076```kotlin 1077@JsonClass(generateAdapter = true) 1078data class BlackjackHand( 1079 val hidden_card: Card, 1080 val visible_cards: List<Card> 1081) 1082``` 1083 1084The codegen adapter requires that your Kotlin types and their properties be either `internal` or 1085`public` (this is Kotlin’s default visibility). 1086 1087Kotlin codegen has no additional runtime dependency. You’ll need to [enable kapt][kapt] and then 1088add the following to your build to enable the annotation processor: 1089 1090```xml 1091<dependency> 1092 <groupId>com.squareup.moshi</groupId> 1093 <artifactId>moshi-kotlin-codegen</artifactId> 1094 <version>1.12.0</version> 1095 <scope>provided</scope> 1096</dependency> 1097``` 1098 1099```kotlin 1100kapt("com.squareup.moshi:moshi-kotlin-codegen:1.13.0") 1101``` 1102 1103You must also have the `kotlin-stdlib` dependency on the classpath during compilation in order for 1104the compiled code to have the required metadata annotations that Moshi's processor looks for. 1105 1106#### Limitations 1107 1108If your Kotlin class has a superclass, it must also be a Kotlin class. Neither reflection or codegen 1109support Kotlin types with Java supertypes or Java types with Kotlin supertypes. If you need to 1110convert such classes to JSON you must create a custom type adapter. 1111 1112The JSON encoding of Kotlin types is the same whether using reflection or codegen. Prefer codegen 1113for better performance and to avoid the `kotlin-reflect` dependency; prefer reflection to convert 1114both private and protected properties. If you have configured both, generated adapters will be used 1115on types that are annotated `@JsonClass(generateAdapter = true)`. 1116 1117Download 1118-------- 1119 1120Download [the latest JAR][dl] or depend via Maven: 1121 1122```xml 1123<dependency> 1124 <groupId>com.squareup.moshi</groupId> 1125 <artifactId>moshi</artifactId> 1126 <version>1.12.0</version> 1127</dependency> 1128``` 1129or Gradle: 1130```kotlin 1131implementation("com.squareup.moshi:moshi:1.13.0") 1132``` 1133 1134Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. 1135 1136 1137R8 / ProGuard 1138-------- 1139 1140Moshi contains minimally required rules for its own internals to work without need for consumers to embed their own. However if you are using reflective serialization and R8 or ProGuard, you must add keep rules in your proguard configuration file for your reflectively serialized classes. 1141 1142#### Enums 1143 1144Annotate enums with `@JsonClass(generateAdapter = false)` to prevent them from being removed/obfuscated from your code by R8/ProGuard. 1145 1146License 1147-------- 1148 1149 Copyright 2015 Square, Inc. 1150 1151 Licensed under the Apache License, Version 2.0 (the "License"); 1152 you may not use this file except in compliance with the License. 1153 You may obtain a copy of the License at 1154 1155 http://www.apache.org/licenses/LICENSE-2.0 1156 1157 Unless required by applicable law or agreed to in writing, software 1158 distributed under the License is distributed on an "AS IS" BASIS, 1159 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1160 See the License for the specific language governing permissions and 1161 limitations under the License. 1162 1163 1164 [dl]: https://search.maven.org/classic/remote_content?g=com.squareup.moshi&a=moshi&v=LATEST 1165 [snap]: https://oss.sonatype.org/content/repositories/snapshots/com/squareup/moshi/ 1166 [okio]: https://github.com/square/okio/ 1167 [okhttp]: https://github.com/square/okhttp/ 1168 [gson]: https://github.com/google/gson/ 1169 [javadoc]: https://square.github.io/moshi/1.x/moshi/ 1170 [kapt]: https://kotlinlang.org/docs/reference/kapt.html 1171