@@ -18,6 +18,7 @@ stable, these are currently experimental features of Kotlin Serialization.
18
18
* [ Integer types] ( #integer-types )
19
19
* [ Lists as repeated fields] ( #lists-as-repeated-fields )
20
20
* [ Packed fields] ( #packed-fields )
21
+ * [ Oneof field (experimental)] ( #oneof-field-experimental )
21
22
* [ ProtoBuf schema generator (experimental)] ( #protobuf-schema-generator-experimental )
22
23
* [ Properties (experimental)] ( #properties-experimental )
23
24
* [ Custom formats (experimental)] ( #custom-formats-experimental )
@@ -435,6 +436,65 @@ Per the standard packed fields can only be used on primitive numeric types. The
435
436
Per the [ format description] ( https://developers.google.com/protocol-buffers/docs/encoding#packed ) the parser ignores
436
437
the annotation, but rather reads list in either packed or repeated format.
437
438
439
+ ### Oneof field (experimental)
440
+
441
+ Kotlin Serialization ` ProtoBuf ` format supports [ oneof] ( https://protobuf.dev/programming-guides/proto2/#oneof ) fields
442
+ base on the [ Polymorphism] ( polymorphism.md ) .
443
+
444
+ You can declare a property of your class to be ` oneof ` by following the contracts:
445
+
446
+ * Declare an interface, or abstract class, in represent of the ` oneof ` group.
447
+ * Declare the property with the type added above, annotated with ` @ProtoOneOf(ids) `
448
+ with all possible proto numbers, not ` @ProtoNumber ` .
449
+ * Declare subclasses from the type with ** only one property** each per the oneof group elements.
450
+ * Annotated the subclasses with ` @ProtoNumber ` on the class declaration, not the property,
451
+ per the oneof group elements and ` @ProtoOneOf(ids) ` above.
452
+
453
+ <!-- - INCLUDE
454
+ import kotlinx.serialization.*
455
+ import kotlinx.serialization.protobuf.*
456
+ -->
457
+
458
+ ``` kotlin
459
+ @Serializable
460
+ data class Data (
461
+ @ProtoNumber(1 ) val name : String ,
462
+ @ProtoOneOf(2 , 3 ) val phone : IPhoneType ,
463
+ )
464
+ @Serializable sealed interface IPhoneType
465
+ @Serializable @ProtoNumber(2 ) @JvmInline value class HomePhone (val number : String ): IPhoneType
466
+ @Serializable @ProtoNumber(3 ) data class WorkPhone (val number : String ): IPhoneType
467
+
468
+ fun main () {
469
+ val dataTom = Data (" Tom" , HomePhone (" 123" ))
470
+ val stringTom = ProtoBuf .encodeToHexString(dataTom)
471
+ val dataJerry = Data (" Jerry" , WorkPhone (" 789" ))
472
+ val stringJerry = ProtoBuf .encodeToHexString(dataJerry)
473
+ println (stringTom)
474
+ println (stringJerry)
475
+ println (ProtoBuf .decodeFromHexString<Data >(stringTom))
476
+ println (ProtoBuf .decodeFromHexString<Data >(stringJerry))
477
+ }
478
+ ```
479
+
480
+ > You can get the full code [ here] ( ../guide/example/example-formats-08.kt ) .
481
+
482
+ ``` text
483
+ 0a03546f6d1203313233
484
+ 0a054a657272791a03373839
485
+ Data(name=Tom, phone=HomePhone(number=123))
486
+ Data(name=Jerry, phone=WorkPhone(number=789))
487
+ ```
488
+
489
+ <!-- - TEST -->
490
+
491
+ In [ ProtoBuf diagnostic mode] ( https://protogen.marcgravell.com/decode ) the first 2 lines on output is equivalent to
492
+
493
+ ```
494
+ Field #1: 0A String Length = 3, Hex = 03, UTF8 = "Tom" Field #2: 12 String Length = 3, Hex = 03, UTF8 = "123"
495
+ Field #1: 0A String Length = 5, Hex = 05, UTF8 = "Jerry" Field #3: 1A String Length = 3, Hex = 03, UTF8 = "789"
496
+ ```
497
+
438
498
### ProtoBuf schema generator (experimental)
439
499
440
500
As mentioned above, when working with protocol buffers you usually use a ".proto" file and a code generator for your
@@ -467,15 +527,15 @@ fun main() {
467
527
println (schemas)
468
528
}
469
529
```
470
- > You can get the full code [ here] ( ../guide/example/example-formats-08 .kt ) .
530
+ > You can get the full code [ here] ( ../guide/example/example-formats-09 .kt ) .
471
531
472
532
Which would output as follows.
473
533
474
534
``` text
475
535
syntax = "proto2";
476
536
477
537
478
- // serial name 'example.exampleFormats08 .SampleData'
538
+ // serial name 'example.exampleFormats09 .SampleData'
479
539
message SampleData {
480
540
required int64 amount = 1;
481
541
optional string description = 2;
@@ -519,7 +579,7 @@ fun main() {
519
579
}
520
580
```
521
581
522
- > You can get the full code [ here] ( ../guide/example/example-formats-09 .kt ) .
582
+ > You can get the full code [ here] ( ../guide/example/example-formats-10 .kt ) .
523
583
524
584
The resulting map has dot-separated keys representing keys of the nested objects.
525
585
@@ -599,7 +659,7 @@ fun main() {
599
659
}
600
660
```
601
661
602
- > You can get the full code [ here] ( ../guide/example/example-formats-10 .kt ) .
662
+ > You can get the full code [ here] ( ../guide/example/example-formats-11 .kt ) .
603
663
604
664
As a result, we got all the primitive values in our object graph visited and put into a list
605
665
in _ serial_ order.
@@ -701,7 +761,7 @@ fun main() {
701
761
}
702
762
```
703
763
704
- > You can get the full code [ here] ( ../guide/example/example-formats-11 .kt ) .
764
+ > You can get the full code [ here] ( ../guide/example/example-formats-12 .kt ) .
705
765
706
766
Now we can convert a list of primitives back to an object tree.
707
767
@@ -792,7 +852,7 @@ fun main() {
792
852
}
793
853
-->
794
854
795
- > You can get the full code [ here] ( ../guide/example/example-formats-12 .kt ) .
855
+ > You can get the full code [ here] ( ../guide/example/example-formats-13 .kt ) .
796
856
797
857
<!-- - TEST
798
858
[kotlinx.serialization, kotlin, 9000]
@@ -899,7 +959,7 @@ fun main() {
899
959
}
900
960
```
901
961
902
- > You can get the full code [ here] ( ../guide/example/example-formats-13 .kt ) .
962
+ > You can get the full code [ here] ( ../guide/example/example-formats-14 .kt ) .
903
963
904
964
We see the size of the list added to the result, letting the decoder know where to stop.
905
965
@@ -1011,7 +1071,7 @@ fun main() {
1011
1071
1012
1072
```
1013
1073
1014
- > You can get the full code [ here] ( ../guide/example/example-formats-14 .kt ) .
1074
+ > You can get the full code [ here] ( ../guide/example/example-formats-15 .kt ) .
1015
1075
1016
1076
In the output we see how not-null` !! ` and ` NULL ` marks are used.
1017
1077
@@ -1139,7 +1199,7 @@ fun main() {
1139
1199
}
1140
1200
```
1141
1201
1142
- > You can get the full code [ here] ( ../guide/example/example-formats-15 .kt ) .
1202
+ > You can get the full code [ here] ( ../guide/example/example-formats-16 .kt ) .
1143
1203
1144
1204
As we can see, the result is a dense binary format that only contains the data that is being serialized.
1145
1205
It can be easily tweaked for any kind of domain-specific compact encoding.
@@ -1333,7 +1393,7 @@ fun main() {
1333
1393
}
1334
1394
```
1335
1395
1336
- > You can get the full code [ here] ( ../guide/example/example-formats-16 .kt ) .
1396
+ > You can get the full code [ here] ( ../guide/example/example-formats-17 .kt ) .
1337
1397
1338
1398
As we can see, our custom byte array format is being used, with the compact encoding of its size in one byte.
1339
1399
0 commit comments