summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrank Piva <pivaf@google.com>2024-03-20 19:07:00 +0000
committerFrank Piva <pivaf@google.com>2024-03-20 19:07:00 +0000
commit06b623f2aacb0c70adc039ef598ee4000162f2e4 (patch)
tree6c05ec1b1480e6abcb95e36ecfe8e5f6b346043e
parente1da81c717e6c6f42231b48e575dafcaf363b971 (diff)
parentdd98219b4909c6279a7cbc12afae03425221ac4f (diff)
downloadkotlinx.serialization-06b623f2aacb0c70adc039ef598ee4000162f2e4.tar.gz
Merge remote-tracking branch 'origin/upstream'
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md23
-rw-r--r--.github/ISSUE_TEMPLATE/feature-request---design-discussion.md12
-rw-r--r--.gitignore17
-rw-r--r--.idea/vcs.xml30
-rw-r--r--Android.bp12
-rw-r--r--CHANGELOG.md963
-rw-r--r--CODE_OF_CONDUCT.md4
-rw-r--r--CONTRIBUTING.md99
l---------LICENSE1
-rw-r--r--LICENSE.txt201
-rw-r--r--METADATA16
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--OWNERS1
-rw-r--r--README.md336
-rw-r--r--RELEASING.md56
-rw-r--r--benchmark/build.gradle32
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/CitmBenchmark.kt35
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/CoerceInputValuesBenchmark.kt63
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/ImplicitNullsBenchmark.kt118
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt96
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/PrimitiveValuesBenchmark.kt61
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterBenchmark.kt41
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedBenchmark.kt52
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedStreamBenchmark.kt82
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/Citm.kt60
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/MacroTwitter.kt184
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/Twitter.kt171
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoBaseline.kt42
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoHuge.kt189
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoListBenchmark.kt35
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoListLikeBenchmark.kt34
-rw-r--r--benchmark/src/jmh/resources/citm_catalog.json50469
-rw-r--r--benchmark/src/jmh/resources/twitter.json1
-rw-r--r--benchmark/src/jmh/resources/twitter_macro.json15482
-rw-r--r--bom/build.gradle41
-rw-r--r--build.gradle189
-rw-r--r--buildSrc/build.gradle.kts24
-rw-r--r--buildSrc/src/main/kotlin/Java9Modularity.kt102
-rw-r--r--buildSrc/src/main/kotlin/Publishing.kt81
-rw-r--r--core/api/kotlinx-serialization-core.api1233
-rw-r--r--core/build.gradle46
-rw-r--r--core/commonMain/src/kotlinx/serialization/Annotations.kt317
-rw-r--r--core/commonMain/src/kotlinx/serialization/ContextualSerializer.kt69
-rw-r--r--core/commonMain/src/kotlinx/serialization/KSerializer.kt177
-rw-r--r--core/commonMain/src/kotlinx/serialization/PolymorphicSerializer.kt109
-rw-r--r--core/commonMain/src/kotlinx/serialization/SealedSerializer.kt147
-rw-r--r--core/commonMain/src/kotlinx/serialization/SerialFormat.kt158
-rw-r--r--core/commonMain/src/kotlinx/serialization/SerializationException.kt84
-rw-r--r--core/commonMain/src/kotlinx/serialization/Serializers.kt182
-rw-r--r--core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt219
-rw-r--r--core/commonMain/src/kotlinx/serialization/builtins/LongAsStringSerializer.kt31
-rw-r--r--core/commonMain/src/kotlinx/serialization/descriptors/ContextAware.kt110
-rw-r--r--core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptor.kt303
-rw-r--r--core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt343
-rw-r--r--core/commonMain/src/kotlinx/serialization/descriptors/SerialKinds.kt262
-rw-r--r--core/commonMain/src/kotlinx/serialization/encoding/AbstractDecoder.kt81
-rw-r--r--core/commonMain/src/kotlinx/serialization/encoding/AbstractEncoder.kt92
-rw-r--r--core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt576
-rw-r--r--core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt511
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/AbstractPolymorphicSerializer.kt113
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/CachedNames.kt20
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/CollectionDescriptors.kt137
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/CollectionSerializers.kt282
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/ElementMarker.kt117
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/Enums.kt91
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/InlineClassDescriptor.kt43
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/InlineClasses.kt70
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/JsonInternalDependencies.kt14
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/NoOpEncoder.kt33
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/NullableSerializer.kt70
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/ObjectSerializer.kt53
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/Platform.common.kt147
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/PluginExceptions.kt40
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor.kt133
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/PluginHelperInterfaces.kt37
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/PrimitiveArraysSerializers.kt410
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/Primitives.kt143
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/SerializationConstructorMarker.kt9
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/Tagged.kt336
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/Tuples.kt170
-rw-r--r--core/commonMain/src/kotlinx/serialization/modules/PolymorphicModuleBuilder.kt116
-rw-r--r--core/commonMain/src/kotlinx/serialization/modules/SerializersModule.kt228
-rw-r--r--core/commonMain/src/kotlinx/serialization/modules/SerializersModuleBuilders.kt259
-rw-r--r--core/commonMain/src/kotlinx/serialization/modules/SerializersModuleCollector.kt91
-rw-r--r--core/commonTest/src/kotlinx/serialization/BasicTypesSerializationTest.kt173
-rw-r--r--core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt37
-rw-r--r--core/commonTest/src/kotlinx/serialization/CustomPropertyAccessorsTest.kt158
-rw-r--r--core/commonTest/src/kotlinx/serialization/ElementMarkerTest.kt97
-rw-r--r--core/commonTest/src/kotlinx/serialization/InheritableSerialInfoTest.kt48
-rw-r--r--core/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt38
-rw-r--r--core/commonTest/src/kotlinx/serialization/SealedGenericClassesTest.kt43
-rw-r--r--core/commonTest/src/kotlinx/serialization/SerialDescriptorAnnotationsTest.kt122
-rw-r--r--core/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt90
-rw-r--r--core/commonTest/src/kotlinx/serialization/SerialDescriptorEqualityTest.kt93
-rw-r--r--core/commonTest/src/kotlinx/serialization/SerialDescriptorSpecificationTest.kt227
-rw-r--r--core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt88
-rw-r--r--core/commonTest/src/kotlinx/serialization/SerializersLookupObjectTest.kt90
-rw-r--r--core/commonTest/src/kotlinx/serialization/TaggedTest.kt58
-rw-r--r--core/commonTest/src/kotlinx/serialization/TestId.kt23
-rw-r--r--core/commonTest/src/kotlinx/serialization/UmbrellaTypes.kt90
-rw-r--r--core/commonTest/src/kotlinx/serialization/WrappedSerialDescriptorTest.kt61
-rw-r--r--core/commonTest/src/kotlinx/serialization/features/SchemaTest.kt138
-rw-r--r--core/commonTest/src/kotlinx/serialization/modules/ContextualGenericsTest.kt74
-rw-r--r--core/commonTest/src/kotlinx/serialization/modules/ModuleBuildersTest.kt337
-rw-r--r--core/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt17
-rw-r--r--core/commonTest/src/kotlinx/serialization/test/TestHelpers.kt44
-rw-r--r--core/jsMain/src/kotlinx/serialization/Serializers.kt14
-rw-r--r--core/jsMain/src/kotlinx/serialization/internal/Platform.kt58
-rw-r--r--core/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt12
-rw-r--r--core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt169
-rw-r--r--core/jvmMain/src/kotlinx/serialization/internal/Platform.kt163
-rw-r--r--core/jvmMainModule/src/module-info.java10
-rw-r--r--core/jvmTest/src/kotlinx/serialization/RetentionTest.kt18
-rw-r--r--core/jvmTest/src/kotlinx/serialization/SerializationMethodInvocationOrderTest.kt186
-rw-r--r--core/jvmTest/src/kotlinx/serialization/SerializeFlatTest.kt300
-rw-r--r--core/jvmTest/src/kotlinx/serialization/features/JvmContextualGenericsTest.kt27
-rw-r--r--core/jvmTest/src/kotlinx/serialization/features/SerializerJvmSpecificTest.kt56
-rw-r--r--core/jvmTest/src/kotlinx/serialization/privateclasstest/PrivateDataOutOfKotlinXSerializationPackageTest.kt47
-rw-r--r--core/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt9
-rw-r--r--core/jvmTest/src/kotlinx/serialization/test/TypeToken.kt19
-rw-r--r--core/nativeMain/src/kotlinx/serialization/Serializers.kt13
-rw-r--r--core/nativeMain/src/kotlinx/serialization/internal/Platform.kt61
-rw-r--r--core/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt11
-rw-r--r--docs/basic-serialization.md701
-rw-r--r--docs/building.md39
-rw-r--r--docs/builtin-classes.md410
-rw-r--r--docs/compatibility.md92
-rw-r--r--docs/formats.md1420
-rw-r--r--docs/inline-classes.md205
-rw-r--r--docs/json.md1093
-rw-r--r--docs/knit.properties2
-rw-r--r--docs/migration.md19
-rw-r--r--docs/polymorphism.md1036
-rw-r--r--docs/serialization-guide.md162
-rw-r--r--docs/serializers.md1158
-rw-r--r--dokka/moduledoc.md52
-rw-r--r--formats/README.md145
-rw-r--r--formats/cbor/api/kotlinx-serialization-cbor.api32
-rw-r--r--formats/cbor/build.gradle33
-rw-r--r--formats/cbor/commonMain/src/kotlinx/serialization/cbor/ByteString.kt26
-rw-r--r--formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt94
-rw-r--r--formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborDecodingException.kt21
-rw-r--r--formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoding.kt683
-rw-r--r--formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Streams.kt87
-rw-r--r--formats/cbor/commonTest/src/kotlinx/serialization/HexConverter.kt51
-rw-r--r--formats/cbor/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt40
-rw-r--r--formats/cbor/commonTest/src/kotlinx/serialization/TestUtilities.kt36
-rw-r--r--formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborNumberEncodingTest.kt215
-rw-r--r--formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborPolymorphismTest.kt50
-rw-r--r--formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborReaderTest.kt737
-rw-r--r--formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborRootLevelNullsTest.kt21
-rw-r--r--formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt125
-rw-r--r--formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt113
-rw-r--r--formats/cbor/jvmMainModule/src/module-info.java6
-rw-r--r--formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborCompatibilityTest.kt132
-rw-r--r--formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborStacktraceRecoveryTest.kt39
-rw-r--r--formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborWriterSpecTest.kt63
-rw-r--r--formats/cbor/jvmTest/src/kotlinx/serialization/cbor/RandomTests.kt215
-rw-r--r--formats/cbor/jvmTest/src/kotlinx/serialization/cbor/TestUtilities.kt43
-rw-r--r--formats/hocon/api/kotlinx-serialization-hocon.api29
-rw-r--r--formats/hocon/build.gradle31
-rw-r--r--formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/Hocon.kt315
-rw-r--r--formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconEncoder.kt140
-rw-r--r--formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconExceptions.kt22
-rw-r--r--formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconSerialKind.kt20
-rw-r--r--formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/NamingConvention.kt13
-rw-r--r--formats/hocon/src/mainModule/kotlin/module-info.java7
-rw-r--r--formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconEncoderTest.kt172
-rw-r--r--formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconNamingConventionTest.kt81
-rw-r--r--formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconObjectsTest.kt151
-rw-r--r--formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconPolymorphismTest.kt105
-rw-r--r--formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconRootObjectsTest.kt96
-rw-r--r--formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconTesting.kt24
-rw-r--r--formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconValuesTest.kt162
-rw-r--r--formats/json/api/kotlinx-serialization-json.api357
-rw-r--r--formats/json/build.gradle32
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/Json.kt318
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/JsonAnnotations.kt72
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/JsonConfiguration.kt42
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/JsonContentPolymorphicSerializer.kt109
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/JsonDecoder.kt91
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt241
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/JsonElementBuilders.kt174
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/JsonElementSerializers.kt234
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/JsonEncoder.kt87
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/JsonTransformingSerializer.kt99
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/Composers.kt87
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonConfiguration.kt0
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonElementMarker.kt32
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonExceptions.kt91
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonNamesMap.kt85
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt141
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt10
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonTreeReader.kt117
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt88
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/PolymorphismValidator.kt90
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/SchemaCache.kt50
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt327
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt216
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt72
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt314
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt236
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/WriteMode.kt52
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt647
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt99
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/ClassWithMultipleMasksTest.kt85
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/EncodingCollectionsTest.kt27
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/EncodingExtensionsTest.kt41
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/EnumSerializationTest.kt133
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/GenericSerializersOnFileTest.kt62
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/JsonOverwriteKeyTest.kt36
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/JsonPathTest.kt164
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/NotNullSerializersCompatibilityOnFileTest.kt109
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt45
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/SerializableClasses.kt30
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/SerializationForNullableTypeOnFileTest.kt60
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/SerializerForNullableTypeTest.kt144
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt260
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/TuplesTest.kt69
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/UmbrellaTypes.kt87
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/UnknownElementIndexTest.kt39
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/builtins/KeyValueSerializersTest.kt142
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/BinaryPayloadExampleTest.kt79
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/ByteArraySerializerTest.kt54
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/CollectionSerializerTest.kt41
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/ContextAndPolymorphicTest.kt136
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/DerivedContextualSerializerTest.kt49
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/GenericCustomSerializerTest.kt111
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/InheritanceTest.kt115
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/JsonClassDiscriminatorTest.kt110
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/JsonNamesTest.kt104
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/LocalClassesTest.kt93
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/LongAsStringTest.kt32
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/ObjectSerialization.kt62
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/PartiallyCustomSerializerTest.kt31
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/PolymorphicOnClassesTest.kt149
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt146
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/PolymorphismWithAnyTest.kt134
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/PrimitiveArraySerializersTest.kt65
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/PropertyInitializerTest.kt105
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/SealedClassesSerializationTest.kt265
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/SealedPolymorphismTest.kt63
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/SerializableOnTypeUsageTest.kt28
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/SerializableWithTest.kt77
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/SkipDefaults.kt68
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/UseSerializersTest.kt32
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt53
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesCompleteTest.kt135
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt189
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/inline/UnsignedIntegersTest.kt59
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/sealed/SealedChild.kt8
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/sealed/SealedParent.kt6
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/AbstractJsonImplicitNullsTest.kt151
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/BasicTypesSerializationTest.kt45
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/DecodeFromJsonElementTest.kt59
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonBuildersTest.kt44
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonCoerceInputValuesTest.kt101
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonConfigurationTest.kt30
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonCustomSerializersTest.kt354
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonDefaultContextTest.kt17
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonElementDecodingTest.kt54
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonEncoderDecoderRecursiveTest.kt211
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonGenericTest.kt55
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonImplicitNullsTest.kt13
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonMapKeysTest.kt150
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonModesTest.kt130
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonNumericKeysTest.kt47
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonOptionalTests.kt53
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt161
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonParserTest.kt118
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonReifiedCollectionsTest.kt29
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonRootLevelNullTest.kt29
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonSealedSubclassTest.kt27
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt143
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonTransformingSerializerTest.kt101
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonTransientTest.kt58
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeAndMapperTest.kt96
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeImplicitNullsTest.kt14
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeTest.kt152
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonUnicodeTest.kt74
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonUnionEnumTest.kt27
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonUpdateModeTest.kt62
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/LenientTest.kt95
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/MapLikeSerializerTest.kt67
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/SpecialFloatingPointValuesTest.kt57
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonContentPolymorphicSerializerTest.kt103
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonDeserializePolymorphicTwiceTest.kt25
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonListPolymorphismTest.kt46
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonMapPolymorphismTest.kt94
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNestedPolymorphismTest.kt67
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNullablePolymorphicTest.kt33
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicClassDescriptorTest.kt26
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicObjectTest.kt44
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt50
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonProhibitedPolymorphicKindsTest.kt96
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPropertyPolymorphicTest.kt63
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt65
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonArraySerializerTest.kt114
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonNativePrimitivesTest.kt29
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonNullSerializerTest.kt61
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonObjectSerializerTest.kt125
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonPrimitiveSerializerTest.kt102
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonSerializerInGenericsTest.kt37
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonTreeTest.kt151
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/serializers/Primitives.kt23
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionInSealedClassesTest.kt54
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionTest.kt124
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/test/ContextualTest.kt47
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt16
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/test/InternalHexConverter.kt51
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/test/JsonHelpers.kt9
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/test/TestHelpers.kt60
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/test/TestId.kt12
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/test/TestingFramework.kt65
-rw-r--r--formats/json/jsMain/src/kotlinx/serialization/json/Dynamics.kt72
-rw-r--r--formats/json/jsMain/src/kotlinx/serialization/json/JsonSchemaCache.kt10
-rw-r--r--formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicDecoders.kt311
-rw-r--r--formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicEncoders.kt285
-rw-r--r--formats/json/jsMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt28
-rw-r--r--formats/json/jsMain/src/kotlinx/serialization/json/internal/createMapForCache.kt12
-rw-r--r--formats/json/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicSpecialCasesTest.kt121
-rw-r--r--formats/json/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicTest.kt200
-rw-r--r--formats/json/jsTest/src/kotlinx/serialization/json/DynamicPolymorphismTest.kt325
-rw-r--r--formats/json/jsTest/src/kotlinx/serialization/json/DynamicToLongTest.kt56
-rw-r--r--formats/json/jsTest/src/kotlinx/serialization/json/EncodeToDynamicSpecialCasesTest.kt92
-rw-r--r--formats/json/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt381
-rw-r--r--formats/json/jsTest/src/kotlinx/serialization/json/JsonCoerceInputValuesDynamicTest.kt98
-rw-r--r--formats/json/jsTest/src/kotlinx/serialization/json/JsonDynamicImplicitNullsTest.kt14
-rw-r--r--formats/json/jsTest/src/kotlinx/serialization/json/JsonNamesDynamicTest.kt79
-rw-r--r--formats/json/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt12
-rw-r--r--formats/json/jsTest/src/kotlinx/serialization/test/JsonHelpers.kt19
-rw-r--r--formats/json/jvmMain/src/kotlinx/serialization/json/JsonSchemaCache.kt10
-rw-r--r--formats/json/jvmMain/src/kotlinx/serialization/json/JvmStreams.kt184
-rw-r--r--formats/json/jvmMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt32
-rw-r--r--formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonIterator.kt99
-rw-r--r--formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonLexerJvm.kt177
-rw-r--r--formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt136
-rw-r--r--formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonToWriterStringBuilder.kt45
-rw-r--r--formats/json/jvmMain/src/kotlinx/serialization/json/internal/createMapForCache.kt15
-rw-r--r--formats/json/jvmMainModule/src/module-info.java6
-rw-r--r--formats/json/jvmTest/resources/corner_cases/listing.txt18
-rw-r--r--formats/json/jvmTest/resources/corner_cases/number_1.0.json1
-rw-r--r--formats/json/jvmTest/resources/corner_cases/number_1.000000000000000005.json1
-rw-r--r--formats/json/jvmTest/resources/corner_cases/number_1000000000000000.json1
-rw-r--r--formats/json/jvmTest/resources/corner_cases/number_10000000000000000999.json1
-rw-r--r--formats/json/jvmTest/resources/corner_cases/number_1e-999.json1
-rw-r--r--formats/json/jvmTest/resources/corner_cases/number_1e6.json1
-rw-r--r--formats/json/jvmTest/resources/corner_cases/object_key_nfc_nfd.json1
-rw-r--r--formats/json/jvmTest/resources/corner_cases/object_key_nfd_nfc.json1
-rw-r--r--formats/json/jvmTest/resources/corner_cases/object_same_key_different_values.json1
-rw-r--r--formats/json/jvmTest/resources/corner_cases/object_same_key_same_value.json1
-rw-r--r--formats/json/jvmTest/resources/corner_cases/object_same_key_unclear_values.json1
-rwxr-xr-xformats/json/jvmTest/resources/corner_cases/string_1_escaped_invalid_codepoint.json1
-rwxr-xr-xformats/json/jvmTest/resources/corner_cases/string_1_invalid_codepoint.json1
-rwxr-xr-xformats/json/jvmTest/resources/corner_cases/string_2_escaped_invalid_codepoints.json1
-rwxr-xr-xformats/json/jvmTest/resources/corner_cases/string_2_invalid_codepoints.json1
-rwxr-xr-xformats/json/jvmTest/resources/corner_cases/string_3_escaped_invalid_codepoints.json1
-rwxr-xr-xformats/json/jvmTest/resources/corner_cases/string_3_invalid_codepoints.json1
-rw-r--r--formats/json/jvmTest/resources/corner_cases/string_with_escaped_NULL.json1
-rw-r--r--formats/json/jvmTest/resources/corpus.zipbin0 -> 427009 bytes
-rw-r--r--formats/json/jvmTest/resources/spec_cases/listing.txt231
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_1_true_without_comma.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_a_invalid_utf8.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_colon_instead_of_comma.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_comma_after_close.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_array_comma_and_number.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_array_double_comma.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_double_extra_comma.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_extra_close.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_extra_comma.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_incomplete.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_incomplete_invalid_value.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_inner_array_no_comma.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_invalid_utf8.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_array_items_separated_by_semicolon.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_array_just_comma.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_array_just_minus.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_missing_value.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_newlines_unclosed.json3
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_array_number_and_comma.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_array_number_and_several_commas.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_array_spaces_vertical_tab_formfeed.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_array_star_inside.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_unclosed.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_unclosed_trailing_comma.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_unclosed_with_new_lines.json3
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_array_unclosed_with_object_inside.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_incomplete_false.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_incomplete_null.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_incomplete_true.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_multidigit_number_then_00.jsonbin0 -> 4 bytes
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_bad_value.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_bracket_key.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_comma_instead_of_colon.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_double_colon.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_emoji.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_garbage_at_end.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_object_key_with_single_quotes.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_lone_continuation_byte_in_key_and_trailing_comma.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_missing_colon.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_object_missing_key.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_object_missing_semicolon.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_missing_value.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_no-colon.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_object_non_string_key.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_object_non_string_key_but_huge_number_instead.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_object_repeated_null_null.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_object_several_trailing_commas.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_single_quote.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_object_trailing_comma.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_open.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open_incomplete.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_object_two_commas_in_a_row.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_unquoted_key.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_unterminated-value.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_with_single_string.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_object_with_trailing_garbage.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_single_space.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1x.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_accentuated_char_no_quotes.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_backslash_00.jsonbin0 -> 6 bytes
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_escape_x.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_string_escaped_backslash_bad.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_escaped_ctrl_char_tab.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_escaped_emoji.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_string_incomplete_escape.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_string_incomplete_escaped_character.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_string_incomplete_surrogate.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_string_incomplete_surrogate_escape_invalid.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_invalid-utf-8-in-escape.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_string_invalid_backslash_esc.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_invalid_unicode_escape.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_invalid_utf8_after_escape.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_string_leading_uescaped_thinspace.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_no_quotes_with_bad_escape.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_string_single_doublequote.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_single_quote.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_string_single_string_no_double_quotes.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_start_escape_unclosed.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_string_unescaped_crtl_char.jsonbin0 -> 7 bytes
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_unescaped_newline.json2
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_unescaped_tab.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_unicode_CapitalU.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_string_with_trailing_garbage.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_100000_opening_arrays.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_U+2060_word_joined.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_UTF8_BOM_no_data.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_angle_bracket_..json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_angle_bracket_null.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_array_trailing_garbage.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_array_with_extra_array_close.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_array_with_unclosed_string.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_ascii-unicode-identifier.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_capitalized_True.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_close_unopened_array.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_comma_instead_of_closing_brace.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_double_array.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_end_array.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_incomplete_UTF8_BOM.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_lone-invalid-utf-8.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_lone-open-bracket.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_no_data.json0
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_null-byte-outside-string.jsonbin0 -> 3 bytes
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_number_with_trailing_garbage.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_object_followed_by_closing_object.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_object_unclosed_no_value.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_object_with_comment.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_object_with_trailing_garbage.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_array_apostrophe.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_array_comma.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_array_object.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_array_open_object.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_array_open_string.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_array_string.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_object.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_open_object_close_array.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_object_comma.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_object_open_array.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_object_open_string.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_object_string_with_apostrophes.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_open_open.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_single_star.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_trailing_#.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_uescaped_LF_before_string.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_partial_null.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_false.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_true.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_unclosed_object.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/n_structure_unicode-identifier.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_whitespace_U+2060_word_joiner.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/n_structure_whitespace_formfeed.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_array_arraysWithSpaces.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_array_empty-string.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_array_empty.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_array_ending_with_newline.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_array_false.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_array_heterogeneous.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_array_null.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_array_with_1_and_newline.json2
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_array_with_leading_space.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_array_with_several_null.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_array_with_trailing_space.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_number_0e+1.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_number_0e1.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_after_space.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_number_double_close_to_zero.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_number_int_with_exp.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_number_minus_zero.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_negative_int.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_negative_one.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_negative_zero.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e_neg_exp.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e_pos_exp.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_real_exponent.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_real_fraction_exponent.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_real_neg_exp.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_real_pos_exponent.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_simple_int.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_number_simple_real.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_object.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_object_basic.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_object_duplicated_key.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_object_duplicated_key_and_value.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_object_empty.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_object_empty_key.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_object_escaped_null_in_key.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_object_extreme_numbers.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_object_long_strings.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_object_simple.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_object_string_unicode.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_object_with_newlines.json3
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_1_2_3_bytes_UTF-8_sequences.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pair.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pairs.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_allowed_escapes.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_backslash_and_u_escaped_zero.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_backslash_doublequotes.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_comments.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_double_escape_a.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_double_escape_n.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_escaped_control_character.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_escaped_noncharacter.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_in_array.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_in_array_with_leading_space.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_last_surrogates_1_and_2.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_nbsp_uescaped.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+10FFFF.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+FFFF.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_null_escape.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_one-byte-utf-8.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_pi.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_reservedCharacterInUTF-8_U+1BFFF.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_simple_ascii.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_space.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_three-byte-utf-8.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_two-byte-utf-8.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_u+2028_line_sep.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_u+2029_par_sep.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_uEscape.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_uescaped_newline.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_unescaped_char_delete.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_unicode.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_unicodeEscapedBackslash.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_unicode_2.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+10FFFE_nonchar.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+1FFFE_nonchar.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+2064_invisible_plus.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+FDD0_nonchar.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+FFFE_nonchar.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_unicode_escaped_double_quote.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_string_utf8.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_string_with_del_character.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_structure_lonely_false.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_structure_lonely_int.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_structure_lonely_negative_real.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_structure_lonely_null.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_structure_lonely_string.json1
-rwxr-xr-xformats/json/jvmTest/resources/spec_cases/y_structure_lonely_true.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_structure_string_empty.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_structure_trailing_newline.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_structure_true_in_array.json1
-rw-r--r--formats/json/jvmTest/resources/spec_cases/y_structure_whitespace_array.json1
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/JavaCollectionsTest.kt52
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/JvmMissingFieldsExceptionTest.kt142
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/SerializationCasesTest.kt93
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/SerializeJavaClassTest.kt48
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/SerializerForNullableJavaTypeTest.kt44
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt81
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/features/ContextualSerializationOnFileTest.kt41
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/features/InternalInheritanceTest.kt127
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/features/JsonJvmStreamsTest.kt88
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/features/JsonLazySequenceTest.kt189
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/features/JsonSequencePathTest.kt41
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt286
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/json/GsonCompatibilityTest.kt56
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/json/ParallelJsonStressTest.kt22
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/json/SpecConformanceTest.ktbin0 -> 5565 bytes
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt8
-rw-r--r--formats/json/jvmTest/src/kotlinx/serialization/test/JsonHelpers.kt20
-rw-r--r--formats/json/nativeMain/src/kotlinx/serialization/json/JsonSchemaCache.kt63
-rw-r--r--formats/json/nativeMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt28
-rw-r--r--formats/json/nativeMain/src/kotlinx/serialization/json/internal/createMapForCache.kt12
-rw-r--r--formats/json/nativeTest/src/kotlinx/serialization/json/MultiWorkerJsonTest.kt55
-rw-r--r--formats/json/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt11
-rw-r--r--formats/json/nativeTest/src/kotlinx/serialization/test/JsonHelpers.kt19
-rw-r--r--formats/properties/api/kotlinx-serialization-properties.api18
-rw-r--r--formats/properties/build.gradle33
-rw-r--r--formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt240
-rw-r--r--formats/properties/commonTest/src/kotlinx/serialization/properties/PropertiesTest.kt240
-rw-r--r--formats/properties/jvmMainModule/src/module-info.java6
-rw-r--r--formats/protobuf/api/kotlinx-serialization-protobuf.api64
-rw-r--r--formats/protobuf/build.gradle50
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt171
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt56
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt87
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/PackedArrayDecoder.kt32
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/PackedArrayEncoder.kt31
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt344
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt210
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt204
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedBase.kt53
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedDecoder.kt107
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt164
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt102
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Streams.kt232
-rw-r--r--formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator.kt525
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/HexConverter.kt18
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt68
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/TestUtils.kt31
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/AutoAssignIdsTest.kt33
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ByteArraySerializerTest.kt52
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomSerializersProtobufTest.kt203
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomizedSerializableTestClasses.kt129
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/MapEntryTest.kt22
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt136
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufAbsenceTest.kt152
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufCollectionsTest.kt104
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufEnumTest.kt56
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufHugeClassTest.kt605
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufMissingFieldsTest.kt125
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufNullAndDefaultTest.kt38
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPolymorphismTest.kt42
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitiveWrappersTest.kt72
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitivesTest.kt31
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ScatteredArraysTest.kt65
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/SerializableTestClasses.kt31
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/schema/SchemaValidationsTest.kt115
-rw-r--r--formats/protobuf/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt16
-rw-r--r--formats/protobuf/jsMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt14
-rw-r--r--formats/protobuf/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt12
-rw-r--r--formats/protobuf/jvmMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt9
-rw-r--r--formats/protobuf/jvmMainModule/src/module-info.java7
-rw-r--r--formats/protobuf/jvmTest/resources/AbstractHolder.proto14
-rw-r--r--formats/protobuf/jvmTest/resources/ContextualHolder.proto8
-rw-r--r--formats/protobuf/jvmTest/resources/FieldNumberClass.proto10
-rw-r--r--formats/protobuf/jvmTest/resources/LegacyMapHolder.proto71
-rw-r--r--formats/protobuf/jvmTest/resources/ListClass.proto23
-rw-r--r--formats/protobuf/jvmTest/resources/MapClass.proto21
-rw-r--r--formats/protobuf/jvmTest/resources/NestedCollections.proto40
-rw-r--r--formats/protobuf/jvmTest/resources/NullableNestedCollections.proto46
-rw-r--r--formats/protobuf/jvmTest/resources/OptionalClass.proto13
-rw-r--r--formats/protobuf/jvmTest/resources/OptionalCollections.proto21
-rw-r--r--formats/protobuf/jvmTest/resources/OptionsClass.proto10
-rw-r--r--formats/protobuf/jvmTest/resources/PackedListClass.proto23
-rw-r--r--formats/protobuf/jvmTest/resources/ScalarHolder.proto21
-rw-r--r--formats/protobuf/jvmTest/resources/SealedHolder.proto27
-rw-r--r--formats/protobuf/jvmTest/resources/SerialNameClass.proto13
-rw-r--r--formats/protobuf/jvmTest/resources/common/schema.proto272
-rw-r--r--formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/FormatConverterHelpers.kt79
-rw-r--r--formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/PolymorphicWithJvmClassTest.kt46
-rw-r--r--formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoBufNullTest.kt235
-rw-r--r--formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoBufOptionalTest.kt119
-rw-r--r--formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoCompatibilityTest.kt26
-rw-r--r--formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtobufEnumWithSerialIdTest.kt32
-rw-r--r--formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtobufTopLevelPrimitivesCompatibilityTest.kt212
-rw-r--r--formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/RandomTests.kt221
-rw-r--r--formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/schema/GenerationTest.kt213
-rw-r--r--formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/schema/RegenerateSchemas.kt45
-rw-r--r--formats/protobuf/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt7
-rw-r--r--formats/protobuf/nativeMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt14
-rw-r--r--formats/protobuf/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt12
-rw-r--r--formats/protobuf/testProto/test_data.proto91
-rw-r--r--gradle.properties40
-rw-r--r--gradle/benchmark-parsing.gradle26
-rw-r--r--gradle/compiler-version.gradle23
-rw-r--r--gradle/configure-source-sets.gradle115
-rw-r--r--gradle/dokka.gradle65
-rw-r--r--gradle/kover.gradle23
-rw-r--r--gradle/maven-metadata.gradle33
-rw-r--r--gradle/native-targets.gradle150
-rw-r--r--gradle/publish-mpp-root-module-in-platform.gradle45
-rw-r--r--gradle/publishing.gradle103
-rw-r--r--gradle/teamcity.gradle14
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 59536 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xgradlew234
-rw-r--r--gradlew.bat89
-rw-r--r--guide/README.md1
-rw-r--r--guide/build.gradle20
-rw-r--r--guide/example/example-basic-01.kt12
-rw-r--r--guide/example/example-basic-02.kt13
-rw-r--r--guide/example/example-basic-03.kt15
-rw-r--r--guide/example/example-builtin-01.kt18
-rw-r--r--guide/example/example-builtin-02.kt13
-rw-r--r--guide/example/example-builtin-03.kt18
-rw-r--r--guide/example/example-builtin-04.kt16
-rw-r--r--guide/example/example-builtin-05.kt16
-rw-r--r--guide/example/example-builtin-06.kt13
-rw-r--r--guide/example/example-builtin-07.kt16
-rw-r--r--guide/example/example-builtin-08.kt16
-rw-r--r--guide/example/example-builtin-09.kt21
-rw-r--r--guide/example/example-builtin-10.kt16
-rw-r--r--guide/example/example-builtin-11.kt15
-rw-r--r--guide/example/example-classes-01.kt23
-rw-r--r--guide/example/example-classes-02.kt20
-rw-r--r--guide/example/example-classes-03.kt19
-rw-r--r--guide/example/example-classes-04.kt15
-rw-r--r--guide/example/example-classes-05.kt15
-rw-r--r--guide/example/example-classes-06.kt20
-rw-r--r--guide/example/example-classes-07.kt15
-rw-r--r--guide/example/example-classes-08.kt15
-rw-r--r--guide/example/example-classes-09.kt13
-rw-r--r--guide/example/example-classes-10.kt25
-rw-r--r--guide/example/example-classes-11.kt13
-rw-r--r--guide/example/example-classes-12.kt15
-rw-r--r--guide/example/example-classes-13.kt17
-rw-r--r--guide/example/example-classes-14.kt17
-rw-r--r--guide/example/example-classes-15.kt22
-rw-r--r--guide/example/example-classes-16.kt13
-rw-r--r--guide/example/example-formats-01.kt21
-rw-r--r--guide/example/example-formats-02.kt17
-rw-r--r--guide/example/example-formats-03.kt25
-rw-r--r--guide/example/example-formats-04.kt21
-rw-r--r--guide/example/example-formats-05.kt26
-rw-r--r--guide/example/example-formats-06.kt25
-rw-r--r--guide/example/example-formats-07.kt23
-rw-r--r--guide/example/example-formats-08.kt18
-rw-r--r--guide/example/example-formats-09.kt18
-rw-r--r--guide/example/example-formats-10.kt36
-rw-r--r--guide/example/example-formats-11.kt62
-rw-r--r--guide/example/example-formats-12.kt64
-rw-r--r--guide/example/example-formats-13.kt72
-rw-r--r--guide/example/example-formats-14.kt78
-rw-r--r--guide/example/example-formats-15.kt94
-rw-r--r--guide/example/example-formats-16.kt135
-rw-r--r--guide/example/example-json-01.kt15
-rw-r--r--guide/example/example-json-02.kt23
-rw-r--r--guide/example/example-json-03.kt17
-rw-r--r--guide/example/example-json-04.kt15
-rw-r--r--guide/example/example-json-05.kt17
-rw-r--r--guide/example/example-json-06.kt19
-rw-r--r--guide/example/example-json-07.kt23
-rw-r--r--guide/example/example-json-08.kt18
-rw-r--r--guide/example/example-json-09.kt17
-rw-r--r--guide/example/example-json-10.kt21
-rw-r--r--guide/example/example-json-11.kt31
-rw-r--r--guide/example/example-json-12.kt12
-rw-r--r--guide/example/example-json-13.kt18
-rw-r--r--guide/example/example-json-14.kt23
-rw-r--r--guide/example/example-json-15.kt17
-rw-r--r--guide/example/example-json-16.kt32
-rw-r--r--guide/example/example-json-17.kt30
-rw-r--r--guide/example/example-json-18.kt22
-rw-r--r--guide/example/example-json-19.kt36
-rw-r--r--guide/example/example-json-20.kt59
-rw-r--r--guide/example/example-json-21.kt37
-rw-r--r--guide/example/example-poly-01.kt15
-rw-r--r--guide/example/example-poly-02.kt15
-rw-r--r--guide/example/example-poly-03.kt17
-rw-r--r--guide/example/example-poly-04.kt18
-rw-r--r--guide/example/example-poly-05.kt18
-rw-r--r--guide/example/example-poly-06.kt19
-rw-r--r--guide/example/example-poly-07.kt21
-rw-r--r--guide/example/example-poly-08.kt19
-rw-r--r--guide/example/example-poly-09.kt29
-rw-r--r--guide/example/example-poly-10.kt28
-rw-r--r--guide/example/example-poly-11.kt31
-rw-r--r--guide/example/example-poly-12.kt29
-rw-r--r--guide/example/example-poly-13.kt28
-rw-r--r--guide/example/example-poly-14.kt29
-rw-r--r--guide/example/example-poly-15.kt34
-rw-r--r--guide/example/example-poly-16.kt38
-rw-r--r--guide/example/example-poly-17.kt48
-rw-r--r--guide/example/example-poly-18.kt30
-rw-r--r--guide/example/example-poly-19.kt37
-rw-r--r--guide/example/example-poly-20.kt74
-rw-r--r--guide/example/example-readme-01.kt18
-rw-r--r--guide/example/example-serializer-01.kt13
-rw-r--r--guide/example/example-serializer-02.kt13
-rw-r--r--guide/example/example-serializer-03.kt17
-rw-r--r--guide/example/example-serializer-04.kt10
-rw-r--r--guide/example/example-serializer-05.kt10
-rw-r--r--guide/example/example-serializer-06.kt13
-rw-r--r--guide/example/example-serializer-07.kt29
-rw-r--r--guide/example/example-serializer-08.kt29
-rw-r--r--guide/example/example-serializer-09.kt34
-rw-r--r--guide/example/example-serializer-10.kt36
-rw-r--r--guide/example/example-serializer-11.kt36
-rw-r--r--guide/example/example-serializer-12.kt52
-rw-r--r--guide/example/example-serializer-13.kt56
-rw-r--r--guide/example/example-serializer-14.kt21
-rw-r--r--guide/example/example-serializer-15.kt28
-rw-r--r--guide/example/example-serializer-16.kt25
-rw-r--r--guide/example/example-serializer-17.kt26
-rw-r--r--guide/example/example-serializer-18.kt22
-rw-r--r--guide/example/example-serializer-19.kt35
-rw-r--r--guide/example/example-serializer-20.kt18
-rw-r--r--guide/example/example-serializer-21.kt28
-rw-r--r--guide/test/BasicSerializationTest.kt144
-rw-r--r--guide/test/BuiltinClassesTest.kt85
-rw-r--r--guide/test/FormatsTest.kt141
-rw-r--r--guide/test/JsonTest.kt163
-rw-r--r--guide/test/PolymorphismTest.kt152
-rw-r--r--guide/test/ReadmeTest.kt15
-rw-r--r--guide/test/SerializersTest.kt156
-rw-r--r--integration-test/build.gradle115
-rw-r--r--integration-test/gradle.properties18
-rw-r--r--integration-test/gradle/wrapper/gradle-wrapper.jarbin0 -> 59536 bytes
-rw-r--r--integration-test/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xintegration-test/gradlew234
-rw-r--r--integration-test/gradlew.bat89
-rw-r--r--integration-test/settings.gradle24
-rw-r--r--integration-test/src/commonMain/kotlin/sample/Data.kt258
-rw-r--r--integration-test/src/commonMain/kotlin/sample/GeoCoordinate.kt20
-rw-r--r--integration-test/src/commonMain/kotlin/sample/MultiFileHierarchyModuleA.kt30
-rw-r--r--integration-test/src/commonMain/kotlin/sample/Sample.kt11
-rw-r--r--integration-test/src/commonTest/kotlin/sample/BasicTypesSerializationTest.kt282
-rw-r--r--integration-test/src/commonTest/kotlin/sample/JsonTest.kt257
-rw-r--r--integration-test/src/commonTest/kotlin/sample/MultiFileHierarchyModuleB.kt63
-rw-r--r--integration-test/src/commonTest/kotlin/sample/MultiFileHierarchyTest.kt76
-rw-r--r--integration-test/src/jsMain/kotlin/sample/SampleJs.kt21
-rw-r--r--integration-test/src/jsTest/kotlin/sample/SampleTestsJS.kt11
-rw-r--r--integration-test/src/jvmMain/kotlin/sample/SampleJvm.kt21
-rw-r--r--integration-test/src/jvmTest/kotlin/sample/SampleTestsJVM.kt24
-rw-r--r--integration-test/src/macosMain/kotlin/sample/SampleMacos.kt21
-rw-r--r--integration-test/src/macosTest/kotlin/sample/SampleTestsNative.kt11
-rw-r--r--knit.properties5
-rw-r--r--kotlin-js-store/yarn.lock560
-rw-r--r--license/LICENSE.txt15
-rw-r--r--license/NOTICE.txt8
-rw-r--r--settings.gradle32
-rwxr-xr-xupdate_docs.sh60
852 files changed, 124085 insertions, 0 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..6e46b037
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,23 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+
+**To Reproduce**
+Attach a code snippet or test data if possible.
+
+**Expected behavior**
+
+**Environment**
+ - Kotlin version: [e.g. 1.3.30]
+ - Library version: [e.g. 0.11.0]
+ - Kotlin platforms: [e.g. JVM, JS, Native or their combinations]
+ - Gradle version: [e.g. 4.10]
+ - IDE version (if bug is related to the IDE) [e.g. IntellijIDEA 2019.1, Android Studio 3.4]
+ - Other relevant context [e.g. OS version, JRE version, ... ]
diff --git a/.github/ISSUE_TEMPLATE/feature-request---design-discussion.md b/.github/ISSUE_TEMPLATE/feature-request---design-discussion.md
new file mode 100644
index 00000000..8c957c47
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature-request---design-discussion.md
@@ -0,0 +1,12 @@
+---
+name: Feature request / design discussion
+about: Suggest an idea for this project
+title: ''
+labels: feature
+assignees: ''
+
+---
+
+**What is your use-case and why do you need this feature?**
+
+**Describe the solution you'd like**
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..c56b7b91
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+.DS_Store
+
+# IntelliJ files
+**/.idea/*
+!/.idea/vcs.xml
+out/
+*.iml
+
+# Gradle files
+build/
+.gradle/
+
+# Maven files
+target
+
+# Modules for JS projects build
+node_modules
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..cc23b035
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CommitMessageInspectionProfile">
+ <profile version="1.0">
+ <inspection_tool class="CommitMessageSpellChecking" enabled="false" level="TYPO" enabled_by_default="true" />
+ <inspection_tool class="GraziCommit" enabled="true" level="TYPO" enabled_by_default="true" />
+ <inspection_tool class="GrazieCommit" enabled="true" level="TYPO" enabled_by_default="true" />
+ </profile>
+ </component>
+ <component name="GithubSharedProjectSettings">
+ <option name="branchProtectionPatterns">
+ <list>
+ <option value="master" />
+ </list>
+ </option>
+ </component>
+ <component name="IssueNavigationConfiguration">
+ <option name="links">
+ <list>
+ <IssueNavigationLink>
+ <option name="issueRegexp" value="#(\d+)" />
+ <option name="linkRegexp" value="https://github.com/Kotlin/kotlinx.serialization/issues/$1" />
+ </IssueNavigationLink>
+ </list>
+ </option>
+ </component>
+ <component name="VcsDirectoryMappings">
+ <mapping directory="" vcs="Git" />
+ </component>
+</project>
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 00000000..efb6bd6f
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,12 @@
+java_library {
+ name: "kotlinx_serializationâ€,
+ host_supported: true,
+ sdk_version: "core_current",
+ srcs: ["kotlinx-serialization-core/jvmMain/src/kotlinx/serialization/**/*.kt"],
+ common_srcs: ["kotlinx-serialization-core/commonMain/src/kotlinx/serialization/**/*.kt"],
+ kotlincflags: [
+ "-Xmulti-platform",
+ "-Xuse-experimental=kotlinx.serialization.ExperimentalSerializationApi",
+ "-Xuse-experimental=kotlinx.serialization.InternalSerializationApi",
+ ],
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..621bf09e
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,963 @@
+1.3.3 / 2022-05-11
+==================
+
+This release contains support for Protocol Buffers packed fields, as well as several bugfixes.
+It uses Kotlin 1.6.21 by default.
+
+### Protobuf packed fields
+
+It is now possible to encode and decode Kotlin classes to/from Protobuf messages with [packed repeated fields](https://developers.google.com/protocol-buffers/docs/encoding#packed).
+To mark the field as packed, use `@ProtoPacked` annotation on it.
+Note it affects only `List` and primitive collection such as `IntArray` types.
+With this feature, it is now possible to decode Proto3 messages, where all repeated fields are packed by default.
+[Protobuf schema generator](https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf.schema/-proto-buf-schema-generator/index.html) also supports new `@ProtoPacked` annotation.
+
+Many thanks to [Paul de Vrieze](https://github.com/pdvrieze) for his valuable contribution!
+
+### Other improvements & small features
+
+ * Incorporate JsonPath into exception messages (#1841)
+ * Mark block in corresponding encodeStructure/decodeStructure extensions as crossinline to reduce amount of bytecode (#1917)
+ * Support serialization of compile-time `Collection<E>` properties that are not lists at the runtime (#1821)
+ * Best-effort kotlin reflect avoidance in serializer(Type) (#1819)
+
+### Bugfixes
+
+ * Iterate over element indices in ObjectSerializer in order to let the format skip unknown keys (#1916)
+ * Correctly support registering both default polymorphic serializer & deserializer (#1849)
+ * Make error message for captured generic type parameters much more straightforward (#1863)
+
+1.3.2 / 2021-12-23
+==================
+
+This release contains several features and bugfixes for core API as well as for HOCON format.
+It uses Kotlin 1.6.10 by default.
+
+### Serializing objects to HOCON
+
+It's now possible to encode Kotlin objects to `Config` values with new `Hocon.encodeToConfig` function.
+This feature may help edit existing configs inside Kotlin program or generate new ones.
+
+Big thanks to [Osip Fatkullin](https://github.com/osipxd) for [implementing](https://github.com/Kotlin/kotlinx.serialization/pull/1740) this.
+
+### Polymorphic default serializers
+
+As of now, `polymorphicDefault` clause inside `SerializersModule { }` builder specifies a
+fallback serializer to be used only during deserialization process. A new function has been introduced to allow setting
+fallback serializer for serialization: `polymorphicDefaultSerializer`.
+This function should ease serializing vast hierarchies of third-party or Java classes.
+
+Note that there are two new experimental functions, `polymorphicDefaultSerializer` and `polymorphicDefaultDeserializer`.
+To avoid naming confusion, we are going to deprecate `polymorphicDefault` in favor of `polymorphicDefaultDeserializer` in the next minor release (1.4.0).
+
+Credit for [the PR](https://github.com/Kotlin/kotlinx.serialization/pull/1686) goes to our contributor [Joseph Burton](https://github.com/Earthcomputer).
+
+### Other improvements
+
+ * HOCON: parse strings into integers and booleans if possible (#1795) (thanks to [tobiaslieber](https://github.com/tobiaslieber))
+ * Add an encodeCollection extensions (#1749) (thanks to [Nicklas Ansman Giertz](https://github.com/ansman))
+
+### Bugfixes
+
+ * Properly handle top-level value classes in encodeToJsonElement (#1777)
+ * Fix incorrect handling of object end when JsonTreeReader (JsonElement) is used with decodeToSequence (#1782)
+
+1.3.1 / 2021-11-11
+==================
+
+This release mainly contains bugfixes for 1.3.0 and provides new experimental `Json.decodeToSequence` function.
+
+### Improvements
+
+ * Provide decodeToSequence to read multiple objects from stream lazily (#1691)
+
+### Bugfixes
+
+ * Correctly handle buffer boundaries while decoding escape sequences from json stream (#1706)
+ * Properly skip unknown keys for objects and structures with zero properties (#1720)
+ * Fix merging for maplikeSerializer when the map is not empty (by using the actual size * 2). (#1712) (thanks to [pdvrieze](https://github.com/pdvrieze))
+ * Fix lookup of primitive array serializers by Java type token (#1708)
+
+
+1.3.0 / 2021-09-23
+==================
+
+This release contains all of the cool new features from 1.3.0-RC (see below) as well as minor improvements.
+It uses Kotlin 1.5.31 by default.
+
+### Bugfixes and improvements
+
+ * Promote JsonConfiguration and its usages to stable (#1690)
+ * Remove opt-in annotations from SerialFormat, StringFormat, BinaryFormat (#1688)
+ * Correctly throw SerializationException instead of IOOBE for some cases with EOF in streams (#1677)
+ * CBOR: ignore tags when reading (#1614) (thanks to [David Robertson](https://github.com/DavidJRobertson))
+
+1.3.0-RC / 2021-09-06
+==================
+
+This is a release candidate for the next version. It contains a lot of interesting features and improvements,
+so we ask you to evaluate it and share your feedback.
+Kotlin 1.5.30 is used by default.
+
+### Java IO stream-based JSON serialization
+
+Finally, in `kotlinx.serialization` 1.3.0 we’re presenting the first experimental version of the serialization API for IO streams:
+`Json.encodeToStream` and `Json.decodeFromStream` extension functions.
+With this API, you can decode objects directly from files, network connections, and other data sources without reading the data to strings beforehand.
+The opposite operation is also available: you can send encoded objects directly to files and other streams in a single API call.
+IO stream serialization is available only on the JVM platform and for the JSON format for now.
+
+Check out more in the [PR](https://github.com/Kotlin/kotlinx.serialization/pull/1569).
+
+### Property-level control over defaults values encoding
+
+Previous versions of the library allowed to specify whether to encode or drop default properties values with
+format configuration flags such as `Json { encodeDefaults = false }`.
+In 1.3.0 we’re extending this feature by adding a new way to fine-tune the serialization of default values:
+you can now control it on the property level using the new `@EncodeDefaults` annotation.
+
+`@EncodeDefaults` annotation has a higher priority over the `encodeDefaults` property and takes one of two possible values:
+- `ALWAYS` (default value) encodes a property value even if it equals to default.
+- `NEVER` doesn’t encode the default value regardless of the format configuration.
+
+Encoding of the annotated properties is not affected by `encodeDefault` format flag
+and works as described for all serialization formats, not only JSON.
+
+To learn more, check corresponding [PR](https://github.com/Kotlin/kotlinx.serialization/pull/1528).
+
+### Excluding null values from JSON serialization
+
+In 1.3.0, we’re introducing one more way to reduce the size of the generated JSON strings: omitting null values.
+A new JSON configuration property `explicitNulls` defines whether `null` property values should be included in the serialized JSON string.
+The difference from `encodeDefaults` is that `explicitNulls = false` flag drops null values even if the property does not have a default value.
+Upon deserializing such a missing property, a `null` or default value (if it exists) will be used.
+
+To maintain backwards compatibility, this flag is set to `true` by default.
+You can learn more in the [documentation](docs/json.md#explicit-nulls) or the [PR](https://github.com/Kotlin/kotlinx.serialization/pull/1535).
+
+### Per-hierarchy polymorphic class discriminators
+
+In previous versions, you could change the discriminator name using the
+[classDiscriminator](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/json.md#class-discriminator) property of the `Json` instance.
+In 1.3.0, we’re adding a way to set a custom discriminator name for each class hierarchy to enable more flexible serialization.
+You can do it by annotating a class with `@JsonClassDiscriminator` with the discriminator name as its argument.
+A custom discriminator is applied to the annotated class and its subclasses.
+Only one custom discriminator can be used in each class hierarchy, thanks to the new `@InheritableSerialInfo` annotation.
+
+Check out corresponding [PR](https://github.com/Kotlin/kotlinx.serialization/pull/1608) for details.
+
+### Support for Java module system
+
+Now all kotlinx.serialization runtime libraries are shipped as a multi-release JAR with `module-info.class` file for Java versions 9 and higher.
+This enables possibilities to use kotlinx.serialization with modern tools such as `jlink` and various technologies such as `TorandoFX`.
+
+Many thanks to our contributor [Gerard de Leeuw](https://github.com/lion7) and his [PR](https://github.com/Kotlin/kotlinx.serialization/pull/1624) for making this possible.
+
+### Native targets for Apple Silicon
+
+This release includes klibs for new targets, introduced in Kotlin/Native 1.5.30 —
+`macosArm64`, `iosSimulatorArm64`, `watchosSimulatorArm64`, and `tvosSimulatorArm64`.
+
+### Bugfixes and improvements
+
+ * Properly handle quoted 'null' literals in lenient mode (#1637)
+ * Switch on deep recursive function when nested level of JSON is too deep (#1596)
+ * Support for local serializable classes in IR compiler
+ * Support default values for `@SerialInfo` annotations in IR compiler
+ * Improve error message for JsonTreeReader (#1597)
+ * Add guide for delegating serializers and wrapping serial descriptor (#1591)
+ * Set target JVM version to 8 for Hocon module in Gradle metadata (#1661)
+
+1.2.2 / 2021-07-08
+==================
+
+This release contains various bugfixes, some useful features and important performance improvements.
+It also uses Kotlin 1.5.20 as default.
+
+### Features
+
+ * Support for `@JsonNames` and `coerceInputValues` in `Json.decodeFromDynamic` (#1479)
+ * Add factory function to wrap a serial descriptor with a custom name for custom delegating serializers (#1547) (thanks to [Fadenfire](https://github.com/Fadenfire))
+ * Allow contextually serialized types to be used as map keys in Json (#1552) (thanks to [pdvrieze](https://github.com/pdvrieze))
+
+### Bugfixes and performance improvements
+
+ * Update size in `JsonStringBuilder` slow-path to avoid excessive array-copies for large strings with escape symbols (#1491)
+ * Optimize integer encoding length in CBOR (#1570) (thanks to [davertay](https://github.com/davertay))
+ * Throw `JsonDecodingException` instead of `ClassCastException` during unexpected null in `TreeJsonDecoder` (#1550)
+ * Prohibit 'null' strings in lenient mode in order to get rid of 'null' and "null" ambiguity (#1549)
+ * Avoid usage of reflective-like `serialDescriptor<KType>` in production sources (#1540)
+ * Added correct error message when deserializing missing enum member for Properties format (#1539)
+ * Make `DescriptorSchemaCache` in Json thread-local on Native (#1484)
+
+1.2.1 / 2021-05-14
+==================
+
+This release mainly contains bugfixes for various issues, including important [broken thread-safety](https://github.com/Kotlin/kotlinx.serialization/issues/1455) and [improper encoding](https://github.com/Kotlin/kotlinx.serialization/issues/1441).
+
+### Features
+
+ * Added support for nullable values, nested and empty collections in protobuf (#1430)
+
+### Bugfixes
+
+ * Support @JsonNames for enum values (#1473)
+ * Handle EOF in skipElement correctly (#1475)
+ * Allow using value classes with primitive carriers as map keys (#1470)
+ * Read JsonNull only for non-string literals in JsonTreeReader (#1466)
+ * Properly reuse JsonStringBuilders in CharArrayPool (#1455)
+ * Properly ensure capacity of the string builder on the append slow-path (#1441)
+
+1.2.0 / 2021-04-27
+==================
+
+**This release has some known critical bugs, so we advise to use 1.2.1 instead.**
+
+This release contains a lot of new features and important improvements listed below;
+Kotlin 1.5.0 is used as a default compiler and language version.
+
+### JSON performance improvements
+
+JSON encoder and decoder were revisited and significantly rewritten,
+which lead us to up to 2-3x times speedup in certain cases.
+Additional details can be found in the corresponding issues: [[1]](https://github.com/Kotlin/kotlinx.serialization/pull/1343), [[2]](https://github.com/Kotlin/kotlinx.serialization/pull/1354).
+
+### Ability to specify alternative names during JSON decoding
+
+[The one of the most voted issues](https://github.com/Kotlin/kotlinx.serialization/issues/203) is fixed now — it is possible to specify multiple names for one property
+using new `@JsonNames` annotation.
+Unlike `@SerialName`, it only affects JSON decoding, so it is useful when dealing with different versions of the API.
+We've prepared a [documentation](https://github.com/Kotlin/kotlinx.serialization/blob/dev/docs/json.md#alternative-json-names) for you about it.
+
+### JsonConfiguration in public API
+
+`JsonConfiguration` is exposed as a property of `Json` instance. You can use it to adjust behavior in
+your [custom serializers](https://github.com/Kotlin/kotlinx.serialization/blob/dev/docs/json.md#maintaining-custom-json-attributes).
+Check out more in the corresponding [issue](https://github.com/Kotlin/kotlinx.serialization/issues/1361) and the [PR](https://github.com/Kotlin/kotlinx.serialization/pull/1409).
+
+### Generator for .proto files based on serializable Kotlin classes
+
+Our implementation of Protocol Buffers format uses `@Serializable` Kotlin classes as a source of schema.
+This is very convenient for Kotlin-to-Kotlin communication, but makes interoperability between languages complicated.
+To resolve this [issue](https://github.com/Kotlin/kotlinx.serialization/issues/34), we now have a
+schema generator that can produce .proto files out of Kotlin classes. Using it, you can keep Kotlin
+classes as a source of truth and use traditional protoc compilers for other languages at the same time.
+To learn more, check out the documentation for the new `ProtoBufSchemaGenerator` class or
+visit the [corresponding PR](https://github.com/Kotlin/kotlinx.serialization/pull/1255).
+
+>Note: this generator is on its experimental stage and any feedback is very welcomed.
+
+### Contextual serialization of generic classes
+
+Before 1.2.0, it was [impossible](https://github.com/Kotlin/kotlinx.serialization/issues/1407) to register context serializer for generic class,
+because `contextual` function accepted a single serializer.
+Now it is possible to register a provider — lambda that allows to construct a serializer for generic class
+out of its type arguments serializers. See the details in the [documentation](https://github.com/Kotlin/kotlinx.serialization/blob/dev/docs/serializers.md#contextual-serialization-and-generic-classes).
+
+### Other features
+
+ * Support for watchosX64 target (#1366).
+ * Introduce kotlinx-serialization-bom (#1356).
+ * Support serializer<T> on JS IR when T is an interface (#1431).
+
+### Bugfixes
+
+ * Fix serializer lookup by KType for third party classes (#1397) (thanks to [mvdbos](https://github.com/mvdbos)).
+ * Fix inability to encode/decode inline class with string to JsonElement (#1408).
+ * Throw SerializationException instead of AIOB in ProtoBuf (#1373).
+ * Fix numeric overflow in JsonLexer (#1367) (thanks to [EdwarDDay](https://github.com/EdwarDDay)).
+
+
+1.1.0 / 2021-02-17
+==================
+
+This release contains all features and bugfixes from 1.1.0-RC plus an additional fix for incorrect exception type
+(#1325 — Throw `SerializationException` instead of `IllegalStateException` in `EnumSerializer`) and uses release version of Kotlin 1.4.30.
+
+In the light of [JCenter shutdown](https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/), starting from 1.1.0-RC and now on,
+all new releases of kotlinx.serialization are published directly to Maven Central and therefore are not available in `https://kotlin.bintray.com/kotlinx/` repository.
+We suggest you to remove `jcenter()` and other kotlin bintray repositories from your buildscripts and to use `mavenCentral()` repository instead.
+
+1.1.0-RC / 2021-02-03
+==================
+
+This is a release candidate of 1.1.0 version. Note that final 1.1.0 version may include more features and bugfixes,
+which would be listed in the corresponding changelog.
+
+### Kotlin version requirement updated
+
+Due to changes in calling conventions between compiler plugin and serialization core runtime, this release requires
+Kotlin version at least 1.4.30-M1. However, this changes should not affect your code,
+because only deprecated functions were removed from public API.
+See [corresponding PR](https://github.com/Kotlin/kotlinx.serialization/pull/1260) for the details.
+
+### Experimental support for inline classes (IR only)
+
+Using 1.1.0-RC, you can mark inline classes as `@Serializable` and use them in other serializable classes.
+Unsigned integer types (`UByte`, `UShort`, `UInt` and `ULong`) are serializable as well and have special support in JSON.
+This feature requires Kotlin compiler 1.4.30-RC and enabling new IR compilers for [JS](https://kotlinlang.org/docs/reference/js-ir-compiler.html) and [JVM](https://kotlinlang.org/docs/reference/whatsnew14.html#new-jvm-ir-backend).
+
+You can learn more in the [documentation](docs/inline-classes.md)
+and corresponding [pull request](https://github.com/Kotlin/kotlinx.serialization/pull/1244).
+
+### Other features
+
+ * Add `serializerOrNull` function for `KType` and `Type` arguments (#1164)
+ * Allow shared prefix names in `Properties` (#1183) (thanks to [TorRanfelt](https://github.com/TorRanfelt))
+ * Add support for encoding/decoding `Properties` values as Strings (#1158) (thanks to [daniel-jasinski](https://github.com/daniel-jasinski))
+
+### Bugfixes and performance improvements
+
+ * Support contextual serialization for derived classes (#1277) (thanks to [Martin Raison](https://github.com/martinraison))
+ * Ensure serialization is usable from K/N background thread (#1282)
+ * Fail on primitive type overflow during `JsonElement` deserialization (#1300)
+ * Throw `SerializationException` instead of ISE when encountering an invalid boolean in JSON (#1299)
+ * Optimize the loop for writing large varints in `ProtoBuf` (#1294)
+ * Fix serializing property with custom accessors and backing field (#1197)
+ * Optimize check for missing fields in deserialization and improve `MissingFieldException` message (#1153)
+ * Improved support of nullable serializer in `@UseSerializers` annotation (#1195)
+ * Correctly escape keys in `JsonObject.toString()` (#1246) (thanks to [Karlatemp](https://github.com/Karlatemp))
+ * Treat `Collection` as `ArrayList` in serializer by type lookups (#1257)
+ * Do not try to end structure in encode/decode structure extensions if an exception has been thrown, so the original exception will be propagated (#1201)
+ * Properly cache serial names in order to improve performance of JSON parser with strict mode (#1209)
+ * Fix dynamic serialization for nullable values (#1199) (thanks to [ankushg](https://github.com/ankushg))
+
+1.0.1 / 2020-10-28
+==================
+
+This patch release contains several feature improvements as well as bugfixes and performance improvements.
+
+### Features
+ * Add object-based serialization and deserialization of polymorphic types for `dynamic` conversions on JS platform (#1122)
+ * Add support for object polymorphism in HOCON decoder (#1136)
+ * Add support of decoding map in the root of HOCON config (#1106)
+
+### Bugfixes
+ * Properly cache generated serializers in PluginGeneratedSerialDescriptor (#1159)
+ * Add Pair and Triple to serializer resolving from Java type token (#1160)
+ * Fix deserialization of half-precision, float and double types in CBOR (#1112)
+ * Fix ByteString annotation detection when ByteArray is nullable (#1139)
+
+1.0.0 / 2020-10-08
+==================
+
+The first public stable release, yay!
+The definitions of stability and backwards compatibility guarantees are located in the [corresponding document](docs/compatibility.md).
+We now also have a GitHub Pages site with [full API reference](https://kotlin.github.io/kotlinx.serialization/).
+
+Compared to RC2, no new features apart from #947 were added and all previously deprecated declarations and migrations were deleted.
+If you are using RC/RC2 along with deprecated declarations, please, migrate before updating to 1.0.0.
+In case you are using pre-1.0 versions (e.g. 0.20.0), please refer to our [migration guide](docs/migration.md).
+
+### Bugfixes and improvements
+
+ * Support nullable types at top-level for JsonElement decoding (#1117)
+ * Add CBOR ignoreUnknownKeys option (#947) (thanks to [Travis Wyatt](https://github.com/twyatt))
+ * Fix incorrect documentation of `encodeDefaults` (#1108) (thanks to [Anders Carling](https://github.com/anderscarling))
+
+1.0.0-RC2 / 2020-09-21
+==================
+
+Second release candidate for 1.0.0 version. This RC contains tweaks and changes based on users feedback after 1.0.0-RC.
+
+### Major changes
+
+* JSON format is now located in different artifact (#994)
+
+In 1.0.0-RC, the `kotlinx-serialization-core` artifact contained core serialization entities as well as `Json` serial format.
+We've decided to change that and to make `core` format-agnostic.
+It would make the life easier for those who use other serial formats and also make possible to write your own implementation of JSON
+or another format without unnecessary dependency on the default one.
+
+In 1.0.0-RC2, `Json` class and related entities are located in `kotlinx-serialization-json` artifact.
+To migrate, simply replace `kotlinx-serialization-core` dependency with `-json`. Core library then will be included automatically
+as the transitive dependency.
+
+For most use-cases, you should use new `kotlinx-serialization-json` artifact. Use `kotlinx-serialization-core` if you are
+writing a library that depends on kotlinx.serialization in a format-agnostic way of provides its own serial format.
+
+* `encodeDefaults` flag is now set to `false` in the default configuration for JSON, CBOR and Protocol Buffers.
+
+The change is motivated by the fact that in most real-life scenarios, this flag is set to `false` anyway,
+because such configuration reduces visual clutter and saves amount of data being serialized.
+Other libraries, like GSON and Moshi, also have this behavior by default.
+
+This may change how your serialized data looks like, if you have not set value for `encodeDefaults` flag explicitly.
+We anticipate that most users already had done this, so no migration is required.
+In case you need to return to the old behavior, simply add `encodeDefaults = true` to your configuration while creating `Json/Cbor/ProtoBuf` object.
+
+* Move `Json.encodeToDynamic/Json.decodeFromDynamic` functions to json package
+
+Since these functions are no longer exposed via `DynamicObjectParser/Serializer` and they are now `Json` class extensions,
+they should be moved to `kotlinx.serialization.json` package.
+To migrate, simply add `import kotlinx.serialization.json.*` to your files.
+
+
+### Bugfixes and improvements
+
+ * Do not provide default implementation for serializersModule in AbstractEncoder/Decoder (#1089)
+ * Support JsonElement hierarchy in `dynamic` encoding/decoding (#1080)
+ * Support top-level primitives and primitive map keys in `dynamic` encoding/decoding
+ * Change core annotations retention (#1083)
+ * Fix 'Duplicate class ... found in modules' on Gradle != 6.1.1 (#996)
+ * Various documentation clarifications
+ * Support deserialization of top-level nullable types (#1038)
+ * Make most serialization exceptions eligible for coroutines exception recovery (#1054)
+ * Get rid of methods that do not present in Android API<24 (#1013, #1040)
+ * Throw JsonDecodingException on empty string literal at the end of the input (#1011)
+ * Remove new lines in deprecation warnings that caused errors in ObjC interop (#990)
+
+1.0.0-RC / 2020-08-17
+==================
+
+Release candidate for 1.0.0 version. The goal of RC release is to collect feedback from users
+and provide 1.0.0 release with bug fixes and improvements based on that feedback.
+
+While working on 1.0.0 version, we carefully examined every public API declaration of the library and
+split it to stable API, that we promise to be source and binary-compatible,
+and experimental API, that may be changed in the future.
+Experimental API is annotated with `@ExperimentalSerializationApi` annotation, which requires opt-in.
+For a more detailed description of the guarantees, please refer to the [compatibility guide](docs/compatibility.md).
+
+The id of the core artifact with `@Serializable` annotation and `Json` format was changed
+from `kotlinx-serialization-runtime` to `kotlinx-serialization-core` to be more clear and aligned with other kotlinx libraries.
+
+A significant part of the public API was renamed or extracted to a separate package.
+To migrate from the previous versions of the library, please refer to the [migration guide](docs/migration.md).
+
+### API changes
+
+#### Json
+
+* Core API changes
+ * `stringify` and `parse` are renamed to `encodeToString` and `decodeFromString`
+ * `parseJson` and `fromJson` are renamed to `parseToJsonElement` and `decodeFromJsonElement`
+ * Reified versions of methods are extracted to extensions
+
+* `Json` constructor is replaced with `Json {}` builder function, `JsonConfiguration` is deprecated in favor
+of `Json {}` builder
+ * All default `Json` implementations are removed
+ * `Json` companion object extends `Json`
+
+* Json configuration
+ * `prettyPrintIndent` allows only whitespaces
+ * `serializeSpecialFloatingPointValues` is renamed to `allowSpecialFloatingPointValues`. It now affects both serialization and deserialization behaviour
+ * `unquoted` JSON flag is deprecated for removal
+ * New `coerceInputValues` option for null-defaults and unknown enums (#90, #246)
+
+* Simplification of `JsonElement` API
+ * Redundant members of `JsonElement` API are deprecated or extracted to extensions
+ * Potential error-prone API is removed
+ * `JsonLiteral` is deprecated in favor of `JsonPrimitive` constructors with nullable parameter
+
+* `JsonElement` builders rework to be aligned with stdlib collection builders (#418, #627)
+ * Deprecated infix `to` and unaryPlus in JSON DSL in favor of `put`/`add` functions
+ * `jsonObject {}` and `json {}` builders are renamed to `buildJsonObject {}` and `buildJsonArray {}`
+ * Make all builders `inline` (#703)
+
+* JavaScript support
+ * `DynamicObjectParser` is deprecated in the favor of `Json.decodeFromDynamic` extension functions
+ * `Json.encodeToDynamic` extension is added as a counterpart to `Json.decodeFromDynamic` (former `DynamicObjectParser`) (#116)
+
+* Other API changes:
+ * `JsonInput` and `JsonOutput` are renamed to `JsonDecoder` and `JsonEncoder`
+ * Methods in `JsonTransformingSerializer` are renamed to `transformSerialize` and `transformDeserialize`
+ * `JsonParametricSerializer` is renamed to `JsonContentPolymorphicSerializer`
+ * `JsonEncodingException` and `JsonDecodingException` are made internal
+
+* Bug fixes
+ * `IllegalStateException` when `null` occurs in JSON input in the place of an expected non-null object (#816)
+ * java.util.NoSuchElementException when deserializing twice from the same JsonElement (#807)
+
+#### Core API for format authoring
+
+* The new naming scheme for `SerialFormats`
+ * Core functions in `StringFormat` and `BinaryFormat` are renamed and now follow the same naming scheme
+ * `stringify`/`parse` are renamed to `encodeToString`/`decodeFromString`
+ * `encodeToByteArray`/`encodeToHexString`/`decodeFromByteArray`/`decodeFromHexString` in `BinaryFormat` are introduced instead of `dump`/`dumps`/`load`/`loads`
+
+* New format instances building convention
+ * Constructors replaced with builder-function with the same name to have the ability to add new configuration parameters,
+ while preserving both source and binary compatibility
+ * Format's companion objects now extend format class and can be used interchangeably
+
+* SerialDescriptor-related API
+ * `SerialDescriptor` and `SerialKind` are moved to a separate `kotlinx.serialization.descriptors` package
+ * `ENUM` and `CONTEXTUAL` kinds now extend `SerialKind` directly
+ * `PrimitiveDescriptor` is renamed to `PrimitiveSerialDescriptor`
+ * Provide specific `buildClassSerialDescriptor` to use with classes' custom serializers, creating other kinds is considered experimental for now
+ * Replace extensions that returned lists (e.g. `elementDescriptors`) with properties that return iterable as an optimization
+ * `IndexOutOfBoundsException` in `descriptor.getElementDescriptor(index)` for `List` after upgrade to 0.20.0 is fixed (#739)
+
+* SerializersModule-related API
+ * `SerialModule` is renamed to `SerializersModule`
+ * `SerialModuleCollector` is renamed to `SerializersModuleCollector`
+ * All builders renamed to be aligned with a single naming scheme (e.g. `SerializersModule {}` DSL)
+ * Deprecate infix `with` in polymorphic builder in favor of subclass()
+ * Helper-like API is extracted to extension functions where possible.
+ * `polymorphicDefault` API for cases when type discriminator is not registered or absent (#902)
+
+* Contextual serialization
+ * `@ContextualSerialization` is split into two annotations: `@Contextual` to use on properties and `@UseContextualSerialization` to use on file
+ * New `SerialDescriptor.capturedKClass` API to introspect SerializersModule-based contextual and polymorphic kinds (#515, #595)
+
+* Encoding-related API
+ * Encoding-related classes (`Encoder`, `Decoder`, `AbstractEncoder`, `AbstractDecoder`) are moved to a separate `kotlinx.serialization.encoding` package
+ * Deprecated `typeParameters` argument in `beginStructure`/`beginCollectio`n methods
+ * Deprecated `updateSerializableValue` and similar methods and `UpdateMode` enum
+ * Renamed `READ_DONE` to `DECODE_DONE`
+ * Make extensions `inline` where applicable
+ * `kotlinx.io` mockery (`InputStream`, `ByteArrayInput`, etc) is removed
+
+* Serializer-related API
+ * `UnitSerializer` is replaced with `Unit.serializer()`
+ * All methods for serializers retrieval are renamed to `serializer`
+ * Context is used as a fallback in `serializer` by KType/Java's Reflect Type functions (#902, #903)
+ * Deprecated all exceptions except `SerializationException`.
+ * `@ImplicitReflectionSerializer` is deprecated
+ * Support of custom serializers for nullable types is added (#824)
+
+#### ProtoBuf
+
+* `ProtoBuf` constructor is replaced with `ProtoBuf {}` builder function
+* `ProtoBuf` companion object now extends `ProtoBuf`
+* `ProtoId` is renamed to `ProtoNumber`, `ProtoNumberType` to `ProtoIntegerType` to be consistent with ProtoBuf specification
+* ProtoBuf performance is significantly (from 2 to 10 times) improved (#216)
+* Top-level primitives, classes and objects are supported in ProtoBuf as length-prefixed tagless messages (#93)
+* `SerializationException` is thrown instead of `IllegalStateException` on incorrect input (#870)
+* `ProtobufDecodingException` is made internal
+
+#### Other formats
+ * All format constructors are migrated to builder scheme
+ * Properties serialize and deserialize enums as strings (#818)
+ * CBOR major type 2 (byte string) support (#842)
+ * `ConfigParser` is renamed to `Hocon`, `kotlinx-serialization-runtime-configparser` artifact is renamed to `kotlinx-serialization-hocon`
+ * Do not write/read size of collection into Properties' map (#743)
+
+0.20.0 / 2020-03-04
+==================
+
+### Release notes
+
+0.20.0 release is focused on giving a library its final and stable API shape.
+
+We have carefully evaluated every `public` declaration and
+decided whether it should be publicly available. As a result, some declarations were deprecated with an intention of removing
+them from public API because they are going to be replaced with others, more valuable and useful for users.
+
+Deprecated symbols include:
+ - Pre-defined JSON instances like `nonStrict` — `strictMode` was split to 3 separate, more granular, flags.
+Users are encouraged to create their own configuration;
+ - Top-level serializers like `IntSerializer` and `ArrayListSerializer`.
+They were replaced with constructor-like factory functions.
+ - `SerialClassDescImpl` creation class replaced with `SerialDescriptor`
+builder function to ease writing of custom serializers and maintain `SerialDescriptor` contract.
+ - Internal utilities, like HexConverter and ByteBuffer, were deprecated as not relevant to serialization public API.
+ - Add-on formats like Protobuf, CBOR and Properties (formerly Mapper)
+are now extracted to [separate artifacts](formats/README.md#protobuf) to keep the core API lightweight.
+
+We have spent a lot of effort into the quality,
+documenting most of the core interfaces, establishing their contracts,
+fixing numerous of bugs, and even introducing new features that may be useful for those of you who
+write custom serializers — see [JsonTransformingSerializer](docs/json_transformations.md).
+
+Such API changes, of course, may be not backwards-compatible in some places, in particular, between compiler plugin and runtime.
+Given that the library is still is in the experimental phase, we took the liberty to introduce breaking changes in order to give users
+the better, more convenient API. Therefore, this release has number `0.20.0` instead of `0.15.0`;
+Kotlin 1.3.70 is compatible _only_ with this release.
+
+To migrate:
+1. Replace `import kotlinx.serialization.internal.*` with `import kotlinx.serialization.builtins.*`.
+This action is sufficient for most of the cases, except primitive serializers — instead of using `IntSerializer`, use `Int.serializer()`.
+For other object-like declarations, you may need to transform it to function call: `ByteArraySerializer` => `ByteArraySerializer()`.
+
+2. Pay attention to the changed `JsonConfiguration` constructor arguments: instead of `strictMode`,
+now three different flags are available: `ignoreUnknownKeys`, `isLenient`, and `serializeSpecialFloatingPointValues`.
+
+3. If you used formats other than JSON, make sure you've included the corresponding artifact as dependency,
+because now they're located outside of core module. See [formats list](formats/README.md) for particular artifact coordinates.
+
+4. Other corresponding deprecation replacements are available via standard `@Deprecated(replaceWith=..)` mechanism.
+(use Alt+Enter for quickfix replacing).
+
+### Full changelog (by commit):
+
+ * This release is compatible with Kotlin 1.3.70
+ * Rework polymorphic descriptors: polymorphic and sealed descriptor elements are now aligned with an actual serialization process (#731)
+ * Hide internal collection and map serializers
+ * Introduce factories for ArraySerializers as well, deprecate top-level array serializers
+ * Extract ElementValue encoder and decoder to builtins and rename it to AbstractEncoder and AbstractDecoder respectively
+ * Hide as much internal API as possible for collections. Now ListSerializer(), etc factories should be used
+ * Replace top-level primitive serializers with corresponding companion functions from builtins
+ * Move Tagged.kt to internal package
+ * Hide tuple serializers from the public usages and replace them with factory methods in builtins package
+ * Deprecate top-level format instances, leave only companion objects
+ * Document contracts for JsonInput/JsonOutput (#715)
+ * Ensure that serialization exception is thrown from JSON parser on invalid inputs (#704)
+ * Do best-effort input/output attach to exceptions to simplify debugging
+ * JSON configuration rework: strictMode is splitted into three flags.
+ * Make strictMode even more restrictive, prohibit unquoted keys and values by default, always use strict boolean parser (#498, #467)
+ * Preserve quotation information during JsonLiteral parsing (#536, #537)
+ * Change MapEntrySerializer.descriptor to be truly map-like. Otherwise, it cannot be properly serialized by TaggedDecoder (-> to JsonObject)
+ * Cleanup ConfigParser: move to proper package to be consistent with other formats
+ * Support primitive and reference arrays in serializer(KType)
+ * Add option to use HOCON naming convention
+ * Allow DynamicObjectParser to handle polymorphic types (array-mode polymorphism only)
+ * Get rid of PrimitiveKind.UNIT and corresponding encoder methods. Now UNIT encoded as regular object.
+ * JsonParametricSerializer and JsonTransformingSerializer implementation
+ * Remove AbstractSerialFormat superclass since it is useless
+ * Deprecate most of the functions intended for internal use
+ * Document core kotlinx.serialization.* package
+ * Introduce UnionKind.CONTEXTUAL to cover Polymorphic/Contextual serializers, get rid of elementsCount in builders
+ * SerialDescriptor for enums rework: now each enum member has object kind
+ * Introduce DSL for creating user-defined serial descriptors
+ * Update README with Gradle Kotlin DSL (#638)
+ * Fix infinite recursion in EnumDescriptor.hashCode() (#666)
+ * Allow duplicating serializers during SerialModule concatenation if they are equal (#616)
+ * Rework sealed class discriminator check to reduce the footprint of the check when no JSON is used
+ * Detect collisions with class discriminator and for equal serial names within the same sealed class hierarchy (#457)
+ * Detect name conflicts in polymorphic serialization during setup phase (#461, #457, #589)
+ * Extract all mutable state in modules package to SerialModuleBuilder to have a single mutable point and to ensure that SerialModule can never be modified
+ * Omit nulls in Properties.store instead of throwing an exception
+ * Add optionals handling to Properties reader (#460, #79)
+ * Support StructureKind.MAP in Properties correctly (#406)
+ * Move Mapper to separate 'properties' module and rename it to Properties
+ * Reified extensions for registering serializers in SerialModule (#671, #669)
+ * Promote KSerializer.nullable to public API
+ * Object serializer support in KType and Type based serializer lookups on JVM (#656)
+ * Deprecate HexConverter
+ * Supply correct child descriptors for Pair and Triple
+ * Rename SerialId to ProtoId to better reflect its semantics
+ * Support of custom generic classes in typeOf()/serializer() API (except JS)
+ * Allow setting `ProtoBuf.shouldEncodeElementDefault` to false (#397, #71)
+ * Add Linux ARM 32 and 64 bit targets
+ * Reduce number of internal dependencies: deprecate IOException, mark IS/OS as internal serialization API (so it can be removed in the future release)
+ * Reduce number of internal dependencies and use bitwise operations in ProtoBuf/Cbor instead of ByteBuffer. Deprecate ByteBuffer for removal
+ * Extract ProtoBuf & CBOR format to the separate module
+ * READ_ALL rework (#600)
+ * SerialDescriptor API standartization (#626, #361, #410)
+ * Support polymorphism in CBOR correctly (fixes #620)
+ * Add forgotten during migration WASM32 target (#625)
+ * Fix exception messages & typos in JsonElement (#621)
+
+v0.14.0 / 2019-11-19
+==================
+
+ * Bump version to 0.14.0 @ Kotlin 1.3.60
+ * Add empty javadoc artifact to linking with Maven Central
+ * Mark more things as @InternalSerializationApi.
+ * Support @SerialId on enum members in protobuf encoding
+ * Move Polymorphic and sealed kinds from UnionKind to special PolymorphicKind
+ * Sealed classes serialization & generated serializers for enum classes (@SerialInfo support)
+ * Objects serialization
+ * Don't use deprecated UTF8<>ByteArray conversions in Native
+ * Improve error message when static non-generic serializer can't be found
+ * Support optional values for typesafe config format
+
+v0.13.0 / 2019-09-12
+==================
+
+ * Add mingwX86 target (#556)
+ * Replace KClass.simpleName with artificial expect/actual with java.lang.Class.simpleName on JVM to overcome requirement for kotlin-reflect.jar (#549)
+ * Update Gradle to 5.6.1 (therefore Gradle metadata to 1.0)
+ * Fix incorrect index supply during map deserialization when READ_ALL was used (#526)
+ * Serializers for primitive arrays (ByteArray etc)
+ * Hide NullableSerializer, introduce '.nullable' extension instead
+ * Fix the library to not create a stack overflow exception when creating a MissingDescriptorException. (#545)
+
+v0.12.0 / 2019-08-23
+==================
+
+ * Set up linuxArm32Hfp target (#535)
+ * wasm32 is added as a build target (#518)
+ * MPP (JVM & Native) serializer resolving from KType (via typeOf()/serializer() function)
+ * Support maps and objects decoding when map size present in stream (fix #517)
+ * Add proper SerialClassDescImpl.toString
+ * Make JSON parser much more stricter; e.g. Prohibit all excessive separators in objects and maps
+ * Robust JsonArray parsing
+ * Improve json exceptions, add more contextual information, get rid of obsolete exception types
+ * Prohibit trailing commas in JSON parser
+ * Make the baseclass of the polymorphic serializer public to allow formats (#520)
+ * Fix decoding for ProtoBuf when there are missing properties in the model. (#506)
+ * Rework JsonException and related subclasses
+ * Fix #480 (deserialization of complex map keys). Add tests for structured map keys in conjuction with polymorphism
+ * Implement 'allowStructuredMapKeys' flag. Now this flag is required for serializing into JSON maps which keys are not primitive.
+
+v0.11.1 / 2019-06-19
+==================
+
+ * Fixed some bugs in compiler plugin for Native (#472, #478) (Kotlin 1.3.40 required)
+ * Remove dependency on stdlib-jvm from common source set (Fixes #481)
+ * Fix @UseSerializers argument type and clarify some docs
+ * Support primitives (ints, strings, JsonLiterals, JsonNull, etc) on a top-level when saving/restoring JSON AST (#451)
+ * Migrate to the new (Kotlin 1.3) MPP model
+ * Add @SharedImmutable to default json module. Fixes #441 and #446
+
+v0.11.0 / 2019-04-12
+====================
+
+#### Plugin:
+
+ * **Semantic change**: Now properties with default values are @Optional by default, and properties without backing fields are @Transient by default.
+ * Allow '@Serializable' on a type usage (fixes #367)
+ * Auto-applying @Polymorphic for interfaces and serializable abstract classes
+ * Do not enable PolymorphicSerializer without special annotation
+ * Fix missing optionality of property when generating descriptor in Native
+ * Fix impossibility to make @Optional field in a class hierarchy on JS
+ * Add synthetic companion with .serializer() getter even if default serializer is overridden. (fixes #228)
+ * Ban primitive arrays in JVM codegen too (fixes #260)
+ * Don't generate writeSelf/internal constructor if corresponding serialize/deserialize aren't auto-generated
+ * Support Serializable class hierarchies on Native and JS
+ * Replace @Optional with @Required
+ * Support classes with more than 32 serializable properties (fixes #164)
+ * Make enums and interfaces not serializable internally. However, they still can be serialized using custom companion object. Fixes #138 and #304
+
+#### Runtime:
+ * Introduce JsonBuilder and JsonConfiguration as a better mechanism for configuring and changing configuration of the JSON
+ * Implement polymorphic serialization in JSON using class discriminator key
+ * Force quoting for map keys (fixes #379)
+ * Fix bug with endianness in Native for Longs/Doubles
+ * Do not allow to mutate SerialModule in formats
+ * Implement multiplatform (JVM, JS and Native) PolymorphicSerializer
+ * Remove obsolete and poorly designed global class cache. Remove JVM-only PolymorphicSerializer
+ * Replace old SerialModule with new one which: - Can not be installed, should be passed in format constructor - Has polymorphic resolve and contextual resolve - Has DSL for creation - Immutable, but can be combined or overwritten
+ * Improve error message for unknown enum constant
+ * Deprecate @Optional, introduce @Required
+ * Use long instead of int in JsonLiteralSerializer
+ * Json and protobuf schemas recording prototype
+ * Change JsonObject so it would comply to a Map interface: .get should return null for a missing key Incompatibility with standard Map contract may bring a lot of problems, e.g. broken equals.
+ * Make JsonElementSerializer public
+
+0.10.0 / 2019-01-22
+==================
+
+ * Migrate to Gradle 4.10 and metadata 0.4
+ * Update to 1.3.20
+ * Reorder Json parameter for consistency
+ * Make JsonElement.toString() consistent with stringify (#325)
+ * Reader.read(): Int should return -1 on EOF.
+ * Optimize the Writer.write(String) case.
+ * Update the docs with new annotations
+
+0.10.0-eap-1 / 2018-12-19
+==================
+
+#### Plugin:
+
+ * Support @SerialInfo annotation for Native
+ * Remove redundant check for 'all parameters are properties' in a case of fully-customized serializer.
+ * Fix unresolved symbol to SerialDescriptor in KSerializer if it was referenced from user custom serializer code (#290)
+ * Support for @UseSerializers annotation
+ * Restrict auto-implementing serializers methods to certain type of classes
+ * Increase priority of overridden serializer on type (#252)
+ * Fix instantiation of generic serializers on JS (#244)
+ * .shouldEncodeElementDefault for JVM (#58)
+ * Support skipping values equals to defaults in output stream for JS and Native backends (#58)
+ * Support enums in Native
+ * Support reference array and context serializers in Native
+ * Fix order of overriding @Serializable(with) on property: check override, than @ContextualSerialization.
+ * Support @Transient properties initializers and init blocks in Native
+ * Better lookup for `serializer()` function in companion for generic classes because user can define a parameterless shorthand one (#228)
+ * Generics serialization in Native
+ * .getElementDescriptor for JVM, JS and Native
+ * Respect @ContextualSerialization on file
+ * Remove auto-applying ContextSerializer. @ContextualSerialization should be used instead.
+
+#### Runtime:
+
+ * Turn around messed endianness names (#308)
+ * Update to Kotlin 1.3.20 EAP 2
+ * Get rid of protobuf-platform functions since @SerialInfo annotations are supported now. Auto-assign ids starting with 1 because 0 is not a valid protobuf ID.
+ * Delegates `equals`, `hashCode` of `JsonObject` and `JsonArray`.
+ * Test for fixed #190 in plugin
+ * UseSerializers annotation
+ * Introduce LongAsStringSerializer
+ * Add validation for parsing dynamic to Long Fixes #274
+ * Merge pull request #294 from Kotlin/recursive_custom_parsing
+ * Fix recursive serialization for JsonOutputs/Inputs
+ * Production-ready JSON API
+ * Remove ValueTransformer
+ * Json improvements
+ * @Serializable support for JsonArray
+ * @Serializable support for JsonObject
+ * @Serializable support for JsonNull and JsonPrimitive
+ * Hide JsonTreeParser, provide Json.parseJson as replacement, implement basic JsonElementSerializer.deserialize
+ * Migrate the rest of the test on JsonTestBase, implement nullable result in tree json
+ * Implement custom serializers support for TreeJsonInput
+ * Implement JsonArray serialization
+ * Implement strict mode for double in TreeJsonOutput (fixes JsonModesTest)
+ * Introduce JsonTestBase in order to ensure streaming and tree json compatibility, transient and strict support in TreeJsonInput
+ * Make JsonElement serializable via common machinery
+ * Json rework, consolidate different parsing mechanisms, hide implementation details
+ * Polymorphic serializer improvements
+ * Renamed identifiers to align with Kotlin's coding conventions. https://kotlinlang.org/docs/reference/coding-conventions.html#naming-rules
+ * Changed JSON -> Json and CBOR -> Cbor
+
+v0.9.1 / 2018-11-19
+==================
+
+ * Update lib to 0.9.1/Kotlin to 1.3.10
+ * Make some clarifications about Gradle plugin DSL and serialization plugin distribution
+ * Primitive descriptor with overriden name
+ * Add missing shorthands for float and char serializers (Fixes #263)
+ * Fix bug where primitive non-string values created by hand and created by parser could be inequal due to a redundant type comparison.
+ * Don't look at default serializer too early during reflective lookup (Fixes #250)
+
+v0.9.0 / 2018-10-24
+==================
+
+ * Fix bug where `.simpleName` was not available for primitives' KClasses.
+ * Improve Mapper: it is now a class (with default instance in Companion) which extends AbstractSerialFormat and therefore have context and proper reflectionless API.
+ * Introduce @ImplicitReflectionSerializer for API which involves reflection.
+ * Add Boolean.Companion.serializer() extension method.
+ * Refactor surface API: introduce interfaces for different formats, move some inline functions for serialization start to extensions. As a minor change, now nulls can be serialized at top-level, where it is supported by the format.
+ * Add AbstractSerialFormat as a base class to all major formats
+ * Update general readme and versions: Library to 0.9, K/N to 1.0 beta
+ * Update documentation for the new API
+ * Updated info about eap13 releases
+
+v0.8.3-rc13 / 2018-10-19
+==================
+
+ * Set default byte order to BigEndian (to be more platform-independent and get rid of posix.BYTE_ORDER dependency)
+ * Update Kotlin version to 1.3-RC4
+ * Remove Gradle metadata from non-native modules
+ * Add missing targets (Fixes #232)
+ * Add license, developer and scm information in Maven pom in publication (Fixes #239)
+ * Add builder for JsonArray
+ * Redesign and unify exceptions from parsers (Fixes #238)
+ * Move json parser back to monolith module (drops `jsonparser` artifact)
+ * Little improvement of error messages
+ > Not working until plugin is updated:
+ * Initial support for skipping defaults: JSON
+ * Replace choicesNames to Array to be easily instantiated from generated IR
+
+v0.8.2-rc13 / 2018-10-03
+========================
+
+ * Update to RC-3
+ * Add @SharedImmutable from K/N to some global declarations in JSON parser, so it is now accessible from multiple workers (Fixes #225)
+ > Not working until plugin is updated:
+ * Tests for generic descriptors
+ * Generated serializer and stuff for providing descriptors from plugin
+ * Tests on @ContextualSerialization on file
+
+v0.8.1-rc13 / 2018-09-24
+========================
+
+ * Upgrade Kotlin/Native version
+
+v0.8.0-rc13 / 2018-09-19
+========================
+
+ * Add (currently) no-op annotations to the kibrary for smoother migration
+ * Update migration guide and versions to RCs.
+ * Support WildcardType in serializerByTypeToken (#212)
+ > Not working until plugin is updated:
+ * Added experimental support of reference arrays for Native
+
+v0.7.3-eap-13 / 2018-09-18
+==========================
+
+ * New enum serializing model
+ * New context: SerialModules draft. Renaming and mutable/immutable hierarchy
+ * Remove untyped encoding
+ * Improve serializers resolving by adding primitive serializers. Also add some helper methods to JSON to serialize lists without pain
+ * Fix protobuf by adapting MapLikeSerializer to HashSetSerializer(MapEntrySerializer). Elements' serializers in collection serializers are now accessible for such adaptions.
+ * Prohibit NaN and infinite values in JSON strict mode
+ * Cleanup JSON, reflect opt-in strict mode in naming
+ * Get rid of StructureKind.SET and StructureKind.ENTRY
+ * Remove SIZE_INDEX
+ * Remove inheritance from Encoder and CompositeEncoder
+ * Working over primitive kinds and enums
+ * Reworked SerialDescriptor and kinds
+ * Renaming of ElementValue* and Tagged*
+ * Renaming: KOutput -> Encoder/CompositeEncoder KInput -> Decoder/CompositeDecoder
+ * Renaming: KSerialClassDesc -> SerialDescriptor SerialSaver, SerialLoader -> *Strategy
+ > Not working until plugin is updated:
+ * Provide limited `equals` on collections' descriptors
+ * Support for `isElementOptional`
+
+v0.6.2 / 2018-09-12
+===================
+
+ * Updated Kotlin to 1.2.70 and Kotlin/Native to 0.9
+
+v0.6.1 / 2018-08-06
+===================
+
+ * Compatibility release for 1.2.60
+ * Don't throw NoSuchElement if key is missing in the map in `Mapper.readNotNullMark`,
+ because tag can be only prefix for nested object. Fixes #182
+ * Update ios sample with latest http client
+
+v0.6.0 / 2018-07-13
+===================
+
+ Plugin:
+
+ * Allow @SerialName and @SerialInfo on classes
+ * Fix resolving serializers for classes from other modules (#153 and #166)
+
+ Runtime:
+
+ * Use new 0.8 K/N DSL
+ * Simplify JSON AST API, Provide JSON builder, provide useful extensions, add documentation, update K/N
+ * Get rid of JsonString to align json primitives with each other. Provide JSON AST pojo parser which exposes current design issues
+ * [JSON-AST] Introduce non-nullable methods throwing exceptions for getting json elements
+ * [JSON-AST] Add ability to parse JSONInput element as tree. Symmetric functionality for JsonOutput + JsonTree
+ * [JSON-AST] Docs writeup
+ * [JSON-AST] Publishing native artifact on bintray
+ * [JSON-AST] Saving AST back to JSON
+ * [JSON-AST] JsonAstMapper to serializable classes
+ * Remove annoying "for class class" message in not found serializer exception
+ * Introduce module for benchmarks
+ * Add notes about snapshot versions
+ * Tests for bugs fixed in latest published plugin (#118 and #125)
+ * Auto-assign proto ids using field index
+
+v0.5.1 / 2018-06-13
+===================
+
+ Plugin:
+
+ * Fix 1.2.50 compatibility
+ * Workaround for recursive resolve on @Serializable(with) and @Serializer(for) pair annotations
+ * Don't generate additional constructor if @SerialInfo has no properties
+ * Fix order of resolving serializers: user-overriden should go before polymorphic and default
+ * While creating descriptors, add type arguments not from serializable class definition but from actual KSerializer implementation. This provides better support for user-defined or external generic serializers
+ * Don't generate constructor for passing generic serializers if user already defined proper one.
+ * Respect `@Serializable(with)` on properties on JS too.
+ * Fix for Kotlin/kotlinx.serialization/136
+ * Fix for Kotlin/kotlinx.serialization/125
+ * Fix for Kotlin/kotlinx.serialization/118
+ * Fix for Kotlin/kotlinx.serialization/123: resolve annotation parameters in-place
+
+ Runtime:
+
+ * Added some shorthands for standard serializers
+ * Fix for bug #141 that uses an extra boolean to determine whether to write a separating comma rather than assuming that the element with the index 0 is written first(or at all) in all cases.
+ * Move mode cache to output class to make .stringify stateless and thread-safe (#139)
+ * Bugfix #95: Can't locate default serializer for classes with named co… (#130)
+ * Updated versions in docs and examples Add changelog
+
+v0.5.0 / 2018-04-26
+===================
+
+ * Improve buildscript and bumped kotlin version to 1.2.40
+ * Remove code warnings
+ * Add note about different IDEA plugin versions
+ * Add null check to Companion when looking up serializer.
+ * Improved performance of JSON.stringify
+ * Improved performance of JSON.parse
+ * Added compatibility note
+ * Fix #107 and #112. #76 awaits next compiler release.
+
+v0.4.2 / 2018-03-07
+===================
+
+ * Update runtime library version to match plugin version. Update examples to use latest version of compiler, plugin and runtime. Update Gradle to run on build agents with Java 9.
+ * Fix ProGuard rules docs for serialization of classes with generic types
+ * Fix ProGuard rules docs for serialization 0.4.1 version
+ * Add support for @Serializable classes that are private and live out of kotlinx.serialization package. In such case the Companion field is not visible and must be set accessible before use.
+ * update jvm-example to latest versions
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..85ed20db
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,4 @@
+## Code of Conduct
+
+This project and the corresponding community is governed by the [JetBrains Open Source and Community Code of Conduct](https://confluence.jetbrains.com/display/ALL/JetBrains+Open+Source+and+Community+Code+of+Conduct). Please make sure you read it.
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..6cd235d3
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,99 @@
+# Contributing Guidelines
+
+There are two main ways to contribute to the project &mdash; submitting issues and submitting
+fixes/changes/improvements via pull requests.
+
+## Submitting issues
+
+Both bug reports and feature requests are welcome.
+Submit issues [here](https://github.com/Kotlin/kotlinx.serialization/issues).
+
+* Search for existing issues to avoid reporting duplicates.
+* When submitting a bug report:
+ * Use a 'bug report' template when creating a new issue.
+ * Test it against the most recently released version. It might have been already fixed.
+ * By default, we assume that your problem reproduces in Kotlin/JVM. Please, mention if the problem is
+ specific to Kotlin/JS or Kotlin/Native.
+ * Include the code that reproduces the problem. Provide the complete reproducer code, yet minimize it as much as possible.
+ * However, don't put off reporting any weird or rarely appearing issues just because you cannot consistently
+ reproduce them.
+ * If the bug is in behavior, then explain what behavior you've expected and what you've got.
+* When submitting a feature request:
+ * Use a 'feature request' template when creating a new issue.
+ * Explain why you need the feature &mdash; what's your use-case, what's your domain.
+ * Explaining the problem you face is more important than suggesting a solution.
+ Report your problem even if you don't have any proposed solution.
+ * If there is an alternative way to do what you need, then show the code of the alternative.
+
+## Submitting PRs
+
+We love PRs. Submit PRs [here](https://github.com/Kotlin/kotlinx.serialization/pulls).
+However, please keep in mind that maintainers will have to support the resulting code of the project,
+so do familiarize yourself with the following guidelines.
+
+* All development (both new features and bug fixes) is performed in the `dev` branch.
+ * The `master` branch always contains sources of the most recently released version.
+ * Base PRs against the `dev` branch.
+ * The `dev` branch is pushed to the `master` branch during release.
+ * Documentation in markdown files can be updated directly in the `master` branch,
+ unless the documentation is in the source code, and the patch changes line numbers.
+* If you fix documentation:
+ * After fixing/changing code examples in the [`docs`](docs) folder or updating any references in the markdown files
+ run the [Knit tool](#running-the-knit-tool) and commit the resulting changes as well.
+ It will not pass the tests otherwise.
+ * If you plan extensive rewrites/additions to the docs, then please [contact the maintainers](#contacting-maintainers)
+ to coordinate the work in advance.
+* If you make any code changes:
+ * Follow the [Kotlin Coding Conventions](https://kotlinlang.org/docs/reference/coding-conventions.html).
+ * Use 4 spaces for indentation.
+ * Use imports with '*'.
+ * [Build the project](#building) to make sure it all works and passes the tests.
+* If you fix a bug:
+ * Write the test the reproduces the bug.
+ * Fixes without tests are accepted only in exceptional circumstances if it can be shown that writing the
+ corresponding test is too hard or otherwise impractical.
+ * Follow the style of writing tests that is used in this project:
+ name test functions as `testXxx`. Don't use backticks in test names.
+* If you introduce any new public APIs:
+ * All new APIs must come with documentation and tests.
+ * All new APIs are initially released with `@ExperimentalSerializationApi` annotation and are graduated later.
+ * [Update the public API dumps](#updating-the-public-api-dump) and commit the resulting changes as well.
+ It will not pass the tests otherwise.
+ * If you plan large API additions, then please start by submitting an issue with the proposed API design
+ to gather community feedback.
+ * [Contact the maintainers](#contacting-maintainers) to coordinate any big piece of work in advance.
+* If you propose/implement a new serialization format:
+ * Follow the general advice on new public APIs above.
+ * Note, that you can keep new format implementation in your own repository to be able to perform proper maintenance
+ and to have a separate release cycle.
+ * You can submit a PR to the [list of community-supported formats](formats/README.md#other-community-supported-formats)
+ with a description of your library.
+* Comment on the existing issue if you want to work on it. Ensure that the issue not only describes a problem,
+ but also describes a solution that had received a positive feedback. Propose a solution if there isn't any.
+
+## Building
+
+You can find all the instructions [here](docs/building.md)
+
+### Running the Knit tool
+
+* Use [Knit](https://github.com/Kotlin/kotlinx-knit/blob/master/README.md) for updates to documentation:
+ * Run `./gradlew knit` to update example files, links, tables of content.
+ * Commit updated documents and examples together with other changes.
+
+### Updating the public API dump
+
+* Use [Binary Compatibility Validator](https://github.com/Kotlin/binary-compatibility-validator/blob/master/README.md) for updates to public API:
+ * Run `./gradlew apiDump` to update API index files.
+ * Commit updated API indexes together with other changes.
+
+## Releases
+
+* Full release procedure checklist is [here](RELEASING.md).
+
+## Contacting maintainers
+
+* If something cannot be done, not convenient, or does not work &mdash; submit an [issue](#submitting-issues).
+* "How to do something" questions &mdash; [StackOverflow](https://stackoverflow.com).
+* Discussions and general inquiries &mdash; use `#serialization` channel in [KotlinLang Slack](https://kotl.in/slack).
+
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 00000000..85de3d45
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE.txt \ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 00000000..9c8f3ea0
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. \ No newline at end of file
diff --git a/METADATA b/METADATA
new file mode 100644
index 00000000..27096d25
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,16 @@
+name: "kotlinx.serialization"
+description:
+ "Kotlin serialization consists of a compiler plugin, that generates visitor "
+ "code for serializable classes, runtime library with core serialization API "
+ "and support libraries with various serialization formats."
+
+third_party {
+ identifier {
+ type: "Git"
+ value: "https://github.com/Kotlin/kotlinx.serialization"
+ primary_source: true
+ version: "v1.3.3"
+ }
+ last_upgrade_date { year: 2024 month: 3 day: 7 }
+ license_type: NOTICE
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 00000000..2e8f086e
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/system/core:main:/janitors/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..31a198a5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,336 @@
+# Kotlin multiplatform / multi-format reflectionless serialization
+
+[![official JetBrains project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
+[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0)
+[![TeamCity build](https://img.shields.io/teamcity/http/teamcity.jetbrains.com/s/KotlinTools_KotlinxSerialization_Ko.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxSerialization_Ko&guest=1)
+[![Kotlin](https://img.shields.io/badge/kotlin-1.6.10-blue.svg?logo=kotlin)](http://kotlinlang.org)
+[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-serialization-core/1.3.3)](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-serialization-core/1.3.3/pom)
+[![KDoc link](https://img.shields.io/badge/API_reference-KDoc-blue)](https://kotlin.github.io/kotlinx.serialization/)
+[![Slack channel](https://img.shields.io/badge/chat-slack-blue.svg?logo=slack)](https://kotlinlang.slack.com/messages/serialization/)
+
+Kotlin serialization consists of a compiler plugin, that generates visitor code for serializable classes,
+ runtime library with core serialization API and support libraries with various serialization formats.
+
+* Supports Kotlin classes marked as `@Serializable` and standard collections.
+* Provides [JSON](formats/README.md#JSON), [Protobuf](formats/README.md#ProtoBuf), [CBOR](formats/README.md#CBOR), [Hocon](formats/README.md#HOCON) and [Properties](formats/README.md#properties) formats.
+* Complete multiplatform support: JVM, JS and Native.
+
+## Table of contents
+
+<!--- TOC -->
+
+* [Introduction and references](#introduction-and-references)
+* [Setup](#setup)
+ * [Gradle](#gradle)
+ * [Using the `plugins` block](#using-the-plugins-block)
+ * [Using `apply plugin` (the old way)](#using-apply-plugin-the-old-way)
+ * [Dependency on the JSON library](#dependency-on-the-json-library)
+ * [Android](#android)
+ * [Multiplatform (Common, JS, Native)](#multiplatform-common-js-native)
+ * [Maven](#maven)
+
+<!--- END -->
+
+* **Additional links**
+ * [Kotlin Serialization Guide](docs/serialization-guide.md)
+ * [Full API reference](https://kotlin.github.io/kotlinx.serialization/)
+
+## Introduction and references
+
+Here is a small example.
+
+```kotlin
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ // Serializing objects
+ val data = Project("kotlinx.serialization", "Kotlin")
+ val string = Json.encodeToString(data)
+ println(string) // {"name":"kotlinx.serialization","language":"Kotlin"}
+ // Deserializing back into objects
+ val obj = Json.decodeFromString<Project>(string)
+ println(obj) // Project(name=kotlinx.serialization, language=Kotlin)
+}
+```
+
+> You can get the full code [here](guide/example/example-readme-01.kt).
+
+<!--- TEST_NAME ReadmeTest -->
+
+<!--- TEST
+{"name":"kotlinx.serialization","language":"Kotlin"}
+Project(name=kotlinx.serialization, language=Kotlin)
+-->
+
+**Read the [Kotlin Serialization Guide](docs/serialization-guide.md) for all details.**
+
+You can find auto-generated documentation website on [GitHub Pages](https://kotlin.github.io/kotlinx.serialization/).
+
+## Setup
+
+Kotlin serialization plugin is shipped with the Kotlin compiler distribution, and the IDEA plugin is bundled into the Kotlin plugin.
+
+Using Kotlin Serialization requires Kotlin compiler `1.4.0` or higher.
+Make sure you have the corresponding Kotlin plugin installed in the IDE, no additional plugins for IDE are required.
+
+### Gradle
+
+#### Using the `plugins` block
+
+You can set up the serialization plugin with the Kotlin plugin using
+[Gradle plugins DSL](https://docs.gradle.org/current/userguide/plugins.html#sec:plugins_block):
+
+Kotlin DSL:
+
+```kotlin
+plugins {
+ kotlin("jvm") version "1.6.10" // or kotlin("multiplatform") or any other kotlin plugin
+ kotlin("plugin.serialization") version "1.6.10"
+}
+```
+
+Groovy DSL:
+
+```gradle
+plugins {
+ id 'org.jetbrains.kotlin.multiplatform' version '1.6.10'
+ id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.10'
+}
+```
+
+> Kotlin versions before 1.4.0 are not supported by the stable release of Kotlin serialization
+
+#### Using `apply plugin` (the old way)
+
+First, you have to add the serialization plugin to your classpath as the other [compiler plugins](https://kotlinlang.org/docs/reference/compiler-plugins.html):
+
+Kotlin DSL:
+
+```kotlin
+buildscript {
+ repositories { mavenCentral() }
+
+ dependencies {
+ val kotlinVersion = "1.6.10"
+ classpath(kotlin("gradle-plugin", version = kotlinVersion))
+ classpath(kotlin("serialization", version = kotlinVersion))
+ }
+}
+```
+
+Groovy DSL:
+
+```gradle
+buildscript {
+ ext.kotlin_version = '1.6.10'
+ repositories { mavenCentral() }
+
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
+ }
+}
+```
+
+Then you can `apply plugin` (example in Groovy):
+
+```gradle
+apply plugin: 'kotlin' // or 'kotlin-multiplatform' for multiplatform projects
+apply plugin: 'kotlinx-serialization'
+```
+
+#### Dependency on the JSON library
+
+After setting up the plugin one way or another, you have to add a dependency on the serialization library.
+Note that while the plugin has version the same as the compiler one, runtime library has different coordinates, repository and versioning.
+
+Kotlin DSL:
+
+```kotlin
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3")
+}
+```
+
+Groovy DSL:
+
+```gradle
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
+}
+```
+
+>We also provide `kotlinx-serialization-core` artifact that contains all serialization API but does not have bundled serialization format with it
+
+### Android
+
+The library works on Android, but, if you're using ProGuard,
+you need to add rules to your `proguard-rules.pro` configuration to cover all classes that are serialized at runtime.
+
+The following configuration keeps serializers for _all_ serializable classes that are retained after shrinking.
+Uncomment and modify the last section in case you're serializing classes with named companion objects.
+
+```proguard
+# Keep `Companion` object fields of serializable classes.
+# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
+-if @kotlinx.serialization.Serializable class **
+-keepclassmembers class <1> {
+ static <1>$Companion Companion;
+}
+
+# Keep `serializer()` on companion objects (both default and named) of serializable classes.
+-if @kotlinx.serialization.Serializable class ** {
+ static **$* *;
+}
+-keepclassmembers class <2>$<3> {
+ kotlinx.serialization.KSerializer serializer(...);
+}
+
+# Keep `INSTANCE.serializer()` of serializable objects.
+-if @kotlinx.serialization.Serializable class ** {
+ public static ** INSTANCE;
+}
+-keepclassmembers class <1> {
+ public static <1> INSTANCE;
+ kotlinx.serialization.KSerializer serializer(...);
+}
+
+# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
+-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
+
+# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
+# If you have any, uncomment and replace classes with those containing named companion objects.
+#-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
+#-if @kotlinx.serialization.Serializable class
+#com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
+#com.example.myapplication.HasNamedCompanion2
+#{
+# static **$* *;
+#}
+#-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
+# static <1>$$serializer INSTANCE;
+#}
+```
+
+In case you want to exclude serializable classes that are used, but never serialized at runtime,
+you will need to write custom rules with narrower [class specifications](https://www.guardsquare.com/manual/configuration/usage).
+
+<details>
+<summary>Example of custom rules</summary>
+
+```proguard
+-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
+
+# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
+-keepclassmembers class kotlinx.serialization.json.** {
+ *** Companion;
+}
+-keepclasseswithmembers class kotlinx.serialization.json.** {
+ kotlinx.serialization.KSerializer serializer(...);
+}
+
+# Application rules
+
+# Change here com.yourcompany.yourpackage
+-keepclassmembers @kotlinx.serialization.Serializable class com.yourcompany.yourpackage.** {
+ # lookup for plugin generated serializable classes
+ *** Companion;
+ # lookup for serializable objects
+ *** INSTANCE;
+ kotlinx.serialization.KSerializer serializer(...);
+}
+# lookup for plugin generated serializable classes
+-if @kotlinx.serialization.Serializable class com.yourcompany.yourpackage.**
+-keepclassmembers class com.yourcompany.yourpackage.<1>$Companion {
+ kotlinx.serialization.KSerializer serializer(...);
+}
+
+# Serialization supports named companions but for such classes it is necessary to add an additional rule.
+# This rule keeps serializer and serializable class from obfuscation. Therefore, it is recommended not to use wildcards in it, but to write rules for each such class.
+-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
+-keep class com.yourcompany.yourpackage.SerializableClassWithNamedCompanion$$serializer {
+ *** INSTANCE;
+}
+```
+</details>
+
+### Multiplatform (Common, JS, Native)
+
+Most of the modules are also available for Kotlin/JS and Kotlin/Native.
+You can add dependency to the required module right to the common source set:
+```gradle
+commonMain {
+ dependencies {
+ // Works as common dependency as well as the platform one
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version"
+ }
+}
+```
+The same artifact coordinates can be used to depend on platform-specific artifact in platform-specific source-set.
+
+### Maven
+
+Ensure the proper version of Kotlin and serialization version:
+
+```xml
+<properties>
+ <kotlin.version>1.6.10</kotlin.version>
+ <serialization.version>1.3.3</serialization.version>
+</properties>
+```
+
+Add serialization plugin to Kotlin compiler plugin:
+
+```xml
+<build>
+ <plugins>
+ <plugin>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-maven-plugin</artifactId>
+ <version>${kotlin.version}</version>
+ <executions>
+ <execution>
+ <id>compile</id>
+ <phase>compile</phase>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <compilerPlugins>
+ <plugin>kotlinx-serialization</plugin>
+ </compilerPlugins>
+ </configuration>
+ <dependencies>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-maven-serialization</artifactId>
+ <version>${kotlin.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+</build>
+```
+
+Add dependency on serialization runtime library:
+
+```xml
+<dependency>
+ <groupId>org.jetbrains.kotlinx</groupId>
+ <artifactId>kotlinx-serialization-json</artifactId>
+ <version>${serialization.version}</version>
+</dependency>
+```
diff --git a/RELEASING.md b/RELEASING.md
new file mode 100644
index 00000000..5b95f0dc
--- /dev/null
+++ b/RELEASING.md
@@ -0,0 +1,56 @@
+# kotlinx.serialization release checklist
+
+To release new `<version>` of `kotlinx.serialization`:
+
+1. Checkout `dev` branch and update:<br>
+ `git checkout dev && git pull`
+
+3. Make sure the `master` branch is fully merged into `dev`:<br>
+ `git merge origin/master`
+
+4. Search & replace `<old-version>` with `<version>` across the project files. Should replace in:
+ * [`README.md`](README.md)
+ * [`gradle.properties`](gradle.properties)
+ * [`integration-test/gradle.properties`](integration-test/gradle.properties)
+
+ Update Kotlin version, if necessary.
+
+5. Write release notes in [`CHANGELOG.md`](CHANGELOG.md):
+ * Use old releases as example of style.
+ * Write each change on a single line (don't wrap with CR).
+ * Study commit message from previous release.
+
+ [git changelog](https://github.com/tj/git-extras/blob/master/Commands.md#git-changelog) from git-extras may help you with that.
+
+6. If necessary, commit your changes to a new branch called `<version>-release` and send it for review, then merge it to `dev` branch.<br>
+If review is not required, commit directly to `dev`.
+
+6. Tag version:<br>
+ `git tag v<version>`
+
+8. Push your changes:<br>
+ `git push origin dev && git push origin --tags`
+
+1. On [TeamCity integration server](https://teamcity.jetbrains.com/project.html?projectId=KotlinTools_KotlinxSerialization&tab=projectOverview):
+ * Wait until "Runtime library (Build – Aggregated)" configuration for committed `dev` branch passes tests.
+ * Run "Runtime library (Deploy - Train)" configuration:
+ * On 'Changes' tab, select `dev` branch and corresponding commit.
+ * On 'Parameters' tab, find 'Deploy version' and fill in with `<version>`.
+
+4. In [Sonatype](https://oss.sonatype.org/#stagingRepositories) admin interface:
+ * Close the repository and wait for it to verify.
+ * Release it.
+
+5. Update documentation website:<br>
+ `./update_docs.sh <version> push`
+
+6. Create a new release in [Github releases](https://github.com/Kotlin/kotlinx.serialization/releases). Use created git tag for title and changelog message for body.
+
+1. Switch back to master branch and update it:<br>
+ ```
+ git checkout master && git pull
+ git merge --ff-only dev
+ git push origin master
+ ```
+
+5. Announce new release in [Slack](https://kotlinlang.slack.com).
diff --git a/benchmark/build.gradle b/benchmark/build.gradle
new file mode 100644
index 00000000..8e0e4927
--- /dev/null
+++ b/benchmark/build.gradle
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+apply plugin: 'java'
+apply plugin: 'kotlin'
+apply plugin: 'kotlinx-serialization'
+apply plugin: 'idea'
+apply plugin: 'net.ltgt.apt'
+apply plugin: 'com.github.johnrengelman.shadow'
+apply plugin: 'me.champeau.gradle.jmh'
+
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+jmh.jmhVersion = 1.22
+
+jmhJar {
+ baseName 'benchmarks'
+ classifier = null
+ version = null
+ destinationDir = file("$rootDir")
+}
+
+dependencies {
+ implementation 'org.openjdk.jmh:jmh-core:1.22'
+ implementation 'com.google.guava:guava:24.1.1-jre'
+ implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.1'
+ implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.12.1'
+ implementation project(':kotlinx-serialization-core')
+ implementation project(':kotlinx-serialization-json')
+ implementation project(':kotlinx-serialization-protobuf')
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/CitmBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/CitmBenchmark.kt
new file mode 100644
index 00000000..1ba89668
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/CitmBenchmark.kt
@@ -0,0 +1,35 @@
+package kotlinx.benchmarks.json
+
+import kotlinx.benchmarks.model.*
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.Json.Default.decodeFromString
+import kotlinx.serialization.json.Json.Default.encodeToString
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+@Warmup(iterations = 7, time = 1)
+@Measurement(iterations = 7, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@State(Scope.Benchmark)
+@Fork(2)
+open class CitmBenchmark {
+ /*
+ * For some reason Citm is kind of de-facto standard cross-language benchmark.
+ * Order of magnitude: 200 ops/sec
+ */
+ private val input = CitmBenchmark::class.java.getResource("/citm_catalog.json").readBytes().decodeToString()
+ private val citm = Json.decodeFromString(CitmCatalog.serializer(), input)
+
+ @Setup
+ fun init() {
+ require(citm == Json.decodeFromString(CitmCatalog.serializer(), Json.encodeToString(citm)))
+ }
+
+ @Benchmark
+ fun decodeCitm(): CitmCatalog = Json.decodeFromString(CitmCatalog.serializer(), input)
+
+ @Benchmark
+ fun encodeCitm(): String = Json.encodeToString(CitmCatalog.serializer(), citm)
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/CoerceInputValuesBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/CoerceInputValuesBenchmark.kt
new file mode 100644
index 00000000..0fe84870
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/CoerceInputValuesBenchmark.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.benchmarks.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@State(Scope.Benchmark)
+@Fork(2)
+open class CoerceInputValuesBenchmark {
+
+ @Serializable
+ class Holder(
+ val i1: Int,
+ val i2: Int,
+ val i3: Int,
+ val i4: Int,
+ val i5: Int,
+ val i6: Int,
+ val i7: Int,
+ val i8: Int,
+ val i9: Int,
+ val i10: Int
+ )
+
+ @Serializable
+ class NullableHolder(
+ val i1: Int?,
+ val i2: Int?,
+ val i3: Int?,
+ val i4: Int?,
+ val i5: Int?,
+ val i6: Int?,
+ val i7: Int?,
+ val i8: Int?,
+ val i9: Int?,
+ val i10: Int?
+ )
+
+ private val str = """{"i1":1,"i2":1,"i3":1,"i4":1,"i5":1,"i6":1,"i7":1,"i8":1,"i9":1,"i10":1}"""
+
+ private val json = Json { coerceInputValues = false }
+ private val coercingJson = Json { coerceInputValues = true }
+
+ @Benchmark
+ fun testNullableCoercing() = coercingJson.decodeFromString(NullableHolder.serializer(), str)
+
+ @Benchmark
+ fun testNullableRegular() = json.decodeFromString(NullableHolder.serializer(), str)
+
+ @Benchmark
+ fun testNonNullableCoercing() = coercingJson.decodeFromString(Holder.serializer(), str)
+
+ @Benchmark
+ fun testNonNullableRegular() = json.decodeFromString(Holder.serializer(), str)
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/ImplicitNullsBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/ImplicitNullsBenchmark.kt
new file mode 100644
index 00000000..11d1240d
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/ImplicitNullsBenchmark.kt
@@ -0,0 +1,118 @@
+package kotlinx.benchmarks.json
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.TimeUnit
+
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@State(Scope.Benchmark)
+@Fork(2)
+open class ImplicitNullsBenchmark {
+
+ @Serializable
+ data class Values(
+ val field0: Int?,
+ val field1: Int?,
+ val field2: Int?,
+ val field3: Int?,
+ val field4: Int?,
+ val field5: Int?,
+ val field6: Int?,
+ val field7: Int?,
+ val field8: Int?,
+ val field9: Int?,
+
+ val field10: Int?,
+ val field11: Int?,
+ val field12: Int?,
+ val field13: Int?,
+ val field14: Int?,
+ val field15: Int?,
+ val field16: Int?,
+ val field17: Int?,
+ val field18: Int?,
+ val field19: Int?,
+
+ val field20: Int?,
+ val field21: Int?,
+ val field22: Int?,
+ val field23: Int?,
+ val field24: Int?,
+ val field25: Int?,
+ val field26: Int?,
+ val field27: Int?,
+ val field28: Int?,
+ val field29: Int?,
+
+ val field30: Int?,
+ val field31: Int?
+ )
+
+
+ private val jsonImplicitNulls = Json { explicitNulls = false }
+
+ private val valueWithNulls = Values(
+ null, null, 2, null, null, null, null, null, null, null,
+ null, null, null, null, 14, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null
+ )
+
+
+ private val jsonWithNulls = """{"field0":null,"field1":null,"field2":2,"field3":null,"field4":null,"field5":null,
+ |"field6":null,"field7":null,"field8":null,"field9":null,"field10":null,"field11":null,"field12":null,
+ |"field13":null,"field14":14,"field15":null,"field16":null,"field17":null,"field18":null,"field19":null,
+ |"field20":null,"field21":null,"field22":null,"field23":null,"field24":null,"field25":null,"field26":null,
+ |"field27":null,"field28":null,"field29":null,"field30":null,"field31":null}""".trimMargin()
+
+ private val jsonNoNulls = """{"field0":0,"field1":1,"field2":2,"field3":3,"field4":4,"field5":5,
+ |"field6":6,"field7":7,"field8":8,"field9":9,"field10":10,"field11":11,"field12":12,
+ |"field13":13,"field14":14,"field15":15,"field16":16,"field17":17,"field18":18,"field19":19,
+ |"field20":20,"field21":21,"field22":22,"field23":23,"field24":24,"field25":25,"field26":26,
+ |"field27":27,"field28":28,"field29":29,"field30":30,"field31":31}""".trimMargin()
+
+ private val jsonWithAbsence = """{"field2":2, "field14":14}"""
+
+ private val serializer = Values.serializer()
+
+ @Benchmark
+ fun decodeNoNulls() {
+ Json.decodeFromString(serializer, jsonNoNulls)
+ }
+
+ @Benchmark
+ fun decodeNoNullsImplicit() {
+ jsonImplicitNulls.decodeFromString(serializer, jsonNoNulls)
+ }
+
+ @Benchmark
+ fun decodeNulls() {
+ Json.decodeFromString(serializer, jsonWithNulls)
+ }
+
+ @Benchmark
+ fun decodeNullsImplicit() {
+ jsonImplicitNulls.decodeFromString(serializer, jsonWithNulls)
+ }
+
+ @Benchmark
+ fun decodeAbsenceImplicit() {
+ jsonImplicitNulls.decodeFromString(serializer, jsonWithAbsence)
+ }
+
+ @Benchmark
+ fun encodeNulls() {
+ Json.encodeToString(serializer, valueWithNulls)
+ }
+
+ @Benchmark
+ fun encodeNullsImplicit() {
+ jsonImplicitNulls.encodeToString(serializer, valueWithNulls)
+ }
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt
new file mode 100644
index 00000000..b8125001
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt
@@ -0,0 +1,96 @@
+package kotlinx.benchmarks.json
+
+import com.fasterxml.jackson.databind.*
+import com.fasterxml.jackson.module.kotlin.*
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+@Warmup(iterations = 7, time = 1)
+@Measurement(iterations = 5, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@State(Scope.Benchmark)
+@Fork(2)
+open class JacksonComparisonBenchmark {
+
+ @Serializable
+ data class DefaultPixelEvent(
+ val version: Int,
+ val dateTime2: String,
+ val serverName: String,
+ val domain: String,
+ val method: String,
+ val clientIp: String,
+ val queryString: String,
+ val userAgent: String,
+ val contentType: String,
+ val browserLanguage: String,
+ val postData: String,
+ val cookies: String
+ )
+
+ private val objectMapper: ObjectMapper = jacksonObjectMapper()
+
+ private val data = DefaultPixelEvent(
+ version = 1,
+ dateTime2 = System.currentTimeMillis().toString(),
+ serverName = "some-endpoint-qwer",
+ domain = "some.domain.com",
+ method = "POST",
+ clientIp = "127.0.0.1",
+ queryString = "anxa=CASCative&anxv=13.901.16.34566&anxe=FoolbarActive&anxt=E7AFBF15-1761-4343-92C1-78167ED19B1C&anxtv=13.901.16.34566&anxp=%5ECQ6%5Expt292%5ES33656%5Eus&anxsi&anxd=2019-10-08T17%3A03%3A57.246Z&f=00400000&anxr=1571945992297&coid=66abafd0d49f42e58dc7536109395306&userSegment&cwsid=opgkcnbminncdgghighmimmphiooeohh",
+ userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0",
+ contentType = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
+ browserLanguage = "en-US,en;q=0.5",
+ postData = "-",
+ cookies = "_ga=GA1.2.971852807.1546968515"
+ )
+
+ private val dataWithEscapes = DefaultPixelEvent(
+ version = 1,
+ dateTime2 = System.currentTimeMillis().toString(),
+ serverName = "some-endp\"oint-qwer",
+ domain = "<a href=\"some.domain.com\">",
+ method = "POST",
+ clientIp = "127.0.0.1",
+ queryString = "anxa=CASCative&anxv=13.901.16.34566&anxe=\"FoolbarActive\"&anxt=E7AFBF15-1761-4343-92C1-78167ED19B1C&anxtv=13.901.16.34566&anxp=%5ECQ6%5Expt292%5ES33656%5Eus&anxsi&anxd=2019-10-08T17%3A03%3A57.246Z&f=00400000&anxr=1571945992297&coid=\"66abafd0d49f42e58dc7536109395306\"&userSegment&cwsid=opgkcnbminncdgghighmimmphiooeohh",
+ userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0",
+ contentType = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
+ browserLanguage = "\"en\"-\"US\",en;\\q=0.5",
+ postData = "-",
+ cookies = "_ga=GA1.2.971852807.1546968515"
+ )
+
+ private val stringData = Json.encodeToString(DefaultPixelEvent.serializer(), data)
+
+ @Serializable
+ private class SmallDataClass(val id: Int, val name: String)
+
+ private val smallData = SmallDataClass(42, "Vincent")
+
+ @Benchmark
+ fun jacksonToString(): String = objectMapper.writeValueAsString(data)
+
+ @Benchmark
+ fun jacksonToStringWithEscapes(): String = objectMapper.writeValueAsString(dataWithEscapes)
+
+ @Benchmark
+ fun jacksonSmallToString(): String = objectMapper.writeValueAsString(smallData)
+
+ @Benchmark
+ fun kotlinToString(): String = Json.encodeToString(DefaultPixelEvent.serializer(), data)
+
+ @Benchmark
+ fun kotlinToStringWithEscapes(): String = Json.encodeToString(DefaultPixelEvent.serializer(), dataWithEscapes)
+
+ @Benchmark
+ fun kotlinSmallToString(): String = Json.encodeToString(SmallDataClass.serializer(), smallData)
+
+ @Benchmark
+ fun jacksonFromString(): DefaultPixelEvent = objectMapper.readValue(stringData, DefaultPixelEvent::class.java)
+
+ @Benchmark
+ fun kotlinFromString(): DefaultPixelEvent = Json.decodeFromString(DefaultPixelEvent.serializer(), stringData)
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/PrimitiveValuesBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/PrimitiveValuesBenchmark.kt
new file mode 100644
index 00000000..6f70565a
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/PrimitiveValuesBenchmark.kt
@@ -0,0 +1,61 @@
+package kotlinx.benchmarks.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+
+@Suppress("unused", "BooleanLiteralArgument")
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@State(Scope.Benchmark)
+@Fork(1)
+open class PrimitiveValuesBenchmark {
+ /*
+ * Stresses int/long and boolean parser.
+ * Order of magnitude: ~1.5 ops/us
+ */
+
+ @Serializable
+ class BooleanHolder(
+ val b1: Boolean, val b2: Boolean, val b3: Boolean, val b4: Boolean,
+ val b5: Boolean, val b6: Boolean, val b7: Boolean, val b8: Boolean
+ )
+
+ private val booleanHolder = BooleanHolder(true, false, true, false, true, true, false, false)
+ private val booleanValue = Json.encodeToString(booleanHolder)
+
+ @Serializable
+ class IntHolder(
+ val b1: Int, val b2: Int, val b3: Int, val b4: Int,
+ val b5: Int, val b6: Int, val b7: Int, val b8: Int
+ )
+
+ private val intHolder = IntHolder(239, step(1), step(2), step(3), step(4), step(5), step(6), step(7))
+ private val intValue = Json.encodeToString(intHolder)
+
+ private fun step(step: Int) = Int.MAX_VALUE / 8 * step
+
+ @Serializable
+ class LongHolder(
+ val b1: Long, val b2: Long, val b3: Long, val b4: Long,
+ val b5: Long, val b6: Long, val b7: Long, val b8: Long
+ )
+
+ private val longHolder = LongHolder(239, step(1L), step(2L), step(3L), step(4L), step(5L), step(6L), step(7L))
+ private val longValue = Json.encodeToString(longHolder)
+
+ private fun step(step: Long) = Long.MAX_VALUE / 8 * step
+
+ @Benchmark
+ fun decodeBoolean(): BooleanHolder = Json.decodeFromString(BooleanHolder.serializer(), booleanValue)
+
+ @Benchmark
+ fun decodeInt(): IntHolder = Json.decodeFromString(IntHolder.serializer(), intValue)
+
+ @Benchmark
+ fun decodeLong(): LongHolder = Json.decodeFromString(LongHolder.serializer(), longValue)
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterBenchmark.kt
new file mode 100644
index 00000000..5c930ec6
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterBenchmark.kt
@@ -0,0 +1,41 @@
+package kotlinx.benchmarks.json
+
+import kotlinx.benchmarks.model.*
+import kotlinx.serialization.json.*
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+@Warmup(iterations = 7, time = 1)
+@Measurement(iterations = 5, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@State(Scope.Benchmark)
+@Fork(2)
+open class TwitterBenchmark {
+
+ /*
+ * Twitter feed benchmark.
+ *
+ * This is a small piece of twitter feed taken from one of the simdjson repository
+ * with Kotlin classes generated by Json2Kotlin plugin (and also manually adjusted)
+ */
+ private val input = TwitterBenchmark::class.java.getResource("/twitter.json").readBytes().decodeToString()
+ private val twitter = Json.decodeFromString(Twitter.serializer(), input)
+
+ private val jsonImplicitNulls = Json { explicitNulls = false }
+
+ @Setup
+ fun init() {
+ require(twitter == Json.decodeFromString(Twitter.serializer(), Json.encodeToString(Twitter.serializer(), twitter)))
+ }
+
+ // Order of magnitude: 4-7 op/ms
+ @Benchmark
+ fun decodeTwitter() = Json.decodeFromString(Twitter.serializer(), input)
+
+ @Benchmark
+ fun decodeTwitterImplicitNulls() = jsonImplicitNulls.decodeFromString(Twitter.serializer(), input)
+
+ @Benchmark
+ fun encodeTwitter() = Json.encodeToString(Twitter.serializer(), twitter)
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedBenchmark.kt
new file mode 100644
index 00000000..e015ad96
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedBenchmark.kt
@@ -0,0 +1,52 @@
+package kotlinx.benchmarks.json
+
+import kotlinx.benchmarks.model.*
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.Json.Default.decodeFromString
+import kotlinx.serialization.json.Json.Default.encodeToString
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+@Warmup(iterations = 7, time = 1)
+@Measurement(iterations = 7, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@State(Scope.Benchmark)
+@Fork(2)
+open class TwitterFeedBenchmark {
+
+ /*
+ * Macro feed benchmark with a lot of UTF-16 used to track general regressions.
+ *
+ * This is a small piece of twitter feed taken from one of the simdjson repository
+ * with Kotlin classes generated by Json2Kotlin plugin (and also manually adjusted)
+ */
+ private val input = TwitterFeedBenchmark::class.java.getResource("/twitter_macro.json").readBytes().decodeToString()
+ private val twitter = Json.decodeFromString(MacroTwitterFeed.serializer(), input)
+ private val jsonNoAltNames = Json { useAlternativeNames = false }
+ private val jsonIgnoreUnknwn = Json { ignoreUnknownKeys = true }
+ private val jsonIgnoreUnknwnNoAltNames = Json { ignoreUnknownKeys = true; useAlternativeNames = false}
+
+ @Setup
+ fun init() {
+ require(twitter == Json.decodeFromString(MacroTwitterFeed.serializer(), Json.encodeToString(MacroTwitterFeed.serializer(), twitter)))
+ }
+
+ // Order of magnitude: ~400 op/s
+ @Benchmark
+ fun decodeTwitter() = Json.decodeFromString(MacroTwitterFeed.serializer(), input)
+
+ @Benchmark
+ fun decodeTwitterNoAltNames() = jsonNoAltNames.decodeFromString(MacroTwitterFeed.serializer(), input)
+
+ @Benchmark
+ fun encodeTwitter() = Json.encodeToString(MacroTwitterFeed.serializer(), twitter)
+
+ @Benchmark
+ fun decodeMicroTwitter() = jsonIgnoreUnknwn.decodeFromString(MicroTwitterFeed.serializer(), input)
+
+ @Benchmark
+ fun decodeMicroTwitterNoAltNames() = jsonIgnoreUnknwnNoAltNames.decodeFromString(MicroTwitterFeed.serializer(), input)
+
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedStreamBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedStreamBenchmark.kt
new file mode 100644
index 00000000..0d9b4cfb
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedStreamBenchmark.kt
@@ -0,0 +1,82 @@
+package kotlinx.benchmarks.json
+
+import com.fasterxml.jackson.databind.DeserializationFeature
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
+import kotlinx.benchmarks.model.MacroTwitterFeed
+import kotlinx.benchmarks.model.MicroTwitterFeed
+import kotlinx.serialization.json.*
+import org.openjdk.jmh.annotations.*
+import java.io.*
+import java.nio.file.Files
+import java.nio.file.Path
+import java.util.concurrent.TimeUnit
+import kotlin.io.path.deleteIfExists
+import kotlin.io.path.outputStream
+
+@Warmup(iterations = 7, time = 1)
+@Measurement(iterations = 7, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@State(Scope.Benchmark)
+@Fork(2)
+open class TwitterFeedStreamBenchmark {
+ val resource = TwitterFeedBenchmark::class.java.getResource("/twitter_macro.json")!!
+ val bytes = resource.readBytes()
+ private val twitter = Json.decodeFromString(MacroTwitterFeed.serializer(), resource.readText())
+
+ private val jsonIgnoreUnknwn = Json { ignoreUnknownKeys = true }
+ private val objectMapper: ObjectMapper =
+ jacksonObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+
+
+ private val inputStream: InputStream
+ get() = ByteArrayInputStream(bytes)
+ private val outputStream: OutputStream
+ get() = ByteArrayOutputStream()
+
+ @Benchmark
+ fun encodeTwitterWriteText(): OutputStream {
+ return outputStream.use {
+ it.bufferedWriter().write(Json.encodeToString(MacroTwitterFeed.serializer(), twitter))
+ it
+ }
+ }
+
+ @Benchmark
+ fun encodeTwitterWriteStream(): OutputStream {
+ return outputStream.use {
+ Json.encodeToStream(MacroTwitterFeed.serializer(), twitter, it)
+ it
+ }
+ }
+
+ @Benchmark
+ fun encodeTwitterJacksonStream(): OutputStream {
+ return outputStream.use {
+ objectMapper.writeValue(it, twitter)
+ it
+ }
+ }
+
+ @Benchmark
+ fun decodeMicroTwitterReadText(): MicroTwitterFeed {
+ return inputStream.use {
+ jsonIgnoreUnknwn.decodeFromString(MicroTwitterFeed.serializer(), it.bufferedReader().readText())
+ }
+ }
+
+ @Benchmark
+ fun decodeMicroTwitterStream(): MicroTwitterFeed {
+ return inputStream.use {
+ jsonIgnoreUnknwn.decodeFromStream(MicroTwitterFeed.serializer(), it.buffered())
+ }
+ }
+
+ @Benchmark
+ fun decodeMicroTwitterJacksonStream(): MicroTwitterFeed {
+ return inputStream.use {
+ objectMapper.readValue(it, MicroTwitterFeed::class.java)
+ }
+ }
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/Citm.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/Citm.kt
new file mode 100644
index 00000000..2eab4a88
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/Citm.kt
@@ -0,0 +1,60 @@
+package kotlinx.benchmarks.model
+
+import kotlinx.serialization.Serializable
+
+
+@Serializable
+data class CitmCatalog(
+ val areaNames: Map<String, String>,
+ val blockNames: Map<String, String>,
+ val events: Map<String, CitmEvent>,
+ val audienceSubCategoryNames: Map<String, String>,
+ val performances: List<CitmPerformance>,
+ val seatCategoryNames: Map<String, String>,
+ val subTopicNames: Map<String, String>,
+ val subjectNames: Map<String, String>,
+ val topicNames: Map<String, String>,
+ val topicSubTopics: Map<String, List<Int>>,
+ val venueNames: Map<String, String>
+)
+
+@Serializable
+data class CitmPerformance(
+ val eventId: Int,
+ val id: Int,
+ val logo: String?,
+ val name: String?,
+ val prices: List<CitmPrice>,
+ val seatCategories: List<CitmSeatCategory>,
+ val seatMapImage: String?,
+ val start: Long,
+ val venueCode: String
+)
+
+@Serializable
+data class CitmSeatCategory(
+ val areas: List<CitmArea>,
+ val seatCategoryId: Int
+)
+
+@Serializable
+data class CitmArea(val areaId: Int, val blockIds: List<String>)
+
+@Serializable
+data class CitmPrice(
+ val amount: Int,
+ val audienceSubCategoryId: Int,
+ val seatCategoryId: Int
+)
+
+@Serializable
+data class CitmEvent(
+ val description: String?,
+ val id: Int?,
+ val logo: String?,
+ val name: String,
+ val subTopicIds: List<Int>,
+ val subjectCode: Int?,
+ val subtitle: String?,
+ val topicIds: List<Int>
+)
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/MacroTwitter.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/MacroTwitter.kt
new file mode 100644
index 00000000..4d7790eb
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/MacroTwitter.kt
@@ -0,0 +1,184 @@
+package kotlinx.benchmarks.model
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class MacroTwitterFeed(
+ val statuses: List<TwitterStatus>,
+ val search_metadata: SearchMetadata
+)
+
+@Serializable
+data class MicroTwitterFeed(
+ val statuses: List<TwitterTrimmedStatus>
+)
+
+@Serializable
+data class TwitterTrimmedStatus(
+ val metadata: Metadata,
+ val created_at: String,
+ val id: Long,
+ val id_str: String,
+ val text: String,
+ val source: String,
+ val truncated: Boolean,
+ val user: TwitterTrimmedUser,
+ val retweeted_status: TwitterTrimmedStatus? = null,
+)
+
+@Serializable
+data class TwitterStatus(
+ val metadata: Metadata,
+ val created_at: String,
+ val id: Long,
+ val id_str: String,
+ val text: String,
+ val source: String,
+ val truncated: Boolean,
+ val in_reply_to_status_id: Long?,
+ val in_reply_to_status_id_str: String?,
+ val in_reply_to_user_id: Long?,
+ val in_reply_to_user_id_str: String?,
+ val in_reply_to_screen_name: String?,
+ val user: TwitterUser,
+ val geo: String?,
+ val coordinates: String?,
+ val place: String?,
+ val contributors: List<String>?,
+ val retweeted_status: TwitterStatus? = null,
+ val retweet_count: Int,
+ val favorite_count: Int,
+ val entities: StatusEntities,
+ val favorited: Boolean,
+ val retweeted: Boolean,
+ val lang: String,
+ val possibly_sensitive: Boolean? = null
+)
+
+@Serializable
+data class StatusEntities(
+ val hashtags: List<Hashtag>,
+ val symbols: List<String>,
+ val urls: List<Url>,
+ val user_mentions: List<TwitterUserMention>,
+ val media: List<TwitterMedia>? = null
+)
+
+@Serializable
+data class TwitterMedia(
+ val id: Long,
+ val id_str: String,
+ val url: String,
+ val media_url: String,
+ val media_url_https: String,
+ val expanded_url: String,
+ val display_url: String,
+ val indices: List<Int>,
+ val type: String,
+ val sizes: SizeType,
+ val source_status_id: Long? = null,
+ val source_status_id_str: String? = null
+)
+
+@Serializable
+data class SizeType(
+ val large: Size,
+ val medium: Size,
+ val thumb: Size,
+ val small: Size
+)
+
+@Serializable
+data class Size(val w: Int, val h: Int, val resize: String)
+
+@Serializable
+data class TwitterUserMention(
+ val screen_name: String,
+ val name: String,
+ val id: Long,
+ val id_str: String,
+ val indices: List<Int>
+)
+
+@Serializable
+data class Urls(val urls: List<Url>)
+
+@Serializable
+data class Metadata(
+ val result_type: String,
+ val iso_language_code: String
+)
+
+@Serializable
+data class TwitterTrimmedUser(
+ val id: Long,
+ val id_str: String,
+ val name: String,
+ val screen_name: String,
+ val location: String,
+ val description: String,
+ val url: String?,
+ val entities: UserEntities,
+ val protected: Boolean,
+ val followers_count: Int,
+ val friends_count: Int,
+ val listed_count: Int,
+ val created_at: String,
+ val favourites_count: Int,
+)
+
+@Serializable
+data class TwitterUser(
+ val id: Long,
+ val id_str: String,
+ val name: String,
+ val screen_name: String,
+ val location: String,
+ val description: String,
+ val url: String?,
+ val entities: UserEntities,
+ val protected: Boolean,
+ val followers_count: Int,
+ val friends_count: Int,
+ val listed_count: Int,
+ val created_at: String,
+ val favourites_count: Int,
+ val utc_offset: Int?,
+ val time_zone: String?,
+ val geo_enabled: Boolean,
+ val verified: Boolean,
+ val statuses_count: Int,
+ val lang: String,
+ val contributors_enabled: Boolean,
+ val is_translator: Boolean,
+ val is_translation_enabled: Boolean,
+ val profile_background_color: String,
+ val profile_background_image_url: String,
+ val profile_background_image_url_https: String,
+ val profile_background_tile: Boolean,
+ val profile_image_url: String,
+ val profile_image_url_https: String,
+ val profile_banner_url: String? = null,
+ val profile_link_color: String,
+ val profile_sidebar_border_color: String,
+ val profile_sidebar_fill_color: String,
+ val profile_text_color: String,
+ val profile_use_background_image: Boolean,
+ val default_profile: Boolean,
+ val default_profile_image: Boolean,
+ val following: Boolean,
+ val follow_request_sent: Boolean,
+ val notifications: Boolean
+)
+
+@Serializable
+data class UserEntities(
+ val url: Urls? = null,
+ val description: Urls
+)
+
+fun main() {
+ val s = MacroTwitterFeed::class.java.getResource("/twitter_macro.json").readBytes().decodeToString()
+ println(Json.decodeFromString<MacroTwitterFeed>(s))
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/Twitter.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/Twitter.kt
new file mode 100644
index 00000000..385afe31
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/Twitter.kt
@@ -0,0 +1,171 @@
+package kotlinx.benchmarks.model
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+fun main() {
+ val s = MacroTwitterFeed::class.java.getResource("/twitter_macro.json").readBytes().decodeToString()
+ println(Json.decodeFromString<MacroTwitterFeed>(s))
+}
+
+/*
+ * Data model for twitter.json feed
+ */
+
+@Serializable
+data class Twitter(
+ val search_metadata: SearchMetadata,
+ val statuses: List<Status>
+)
+
+@Serializable
+data class SearchMetadata(
+ val completed_in: Double,
+ val count: Int,
+ val max_id: Long,
+ val max_id_str: String,
+ val next_results: String,
+ val query: String,
+ val refresh_url: String,
+ val since_id: Int,
+ val since_id_str: String
+)
+
+@Serializable
+data class Status(
+ val created_at: String,
+ val entities: Entities,
+ val favorited: Boolean,
+ val id: Long,
+ val id_str: String,
+ val possibly_sensitive: Boolean = false,
+ val retweet_count: Int,
+ val retweeted: Boolean,
+ val source: String,
+ val text: String,
+ val truncated: Boolean,
+ val user: User
+)
+
+@Serializable
+data class Entities(
+ val hashtags: List<Hashtag>,
+ val media: List<Media> = emptyList(),
+ val urls: List<Url>,
+ val user_mentions: List<UserMention>
+)
+
+@Serializable
+data class User(
+ val contributors_enabled: Boolean,
+ val created_at: String,
+ val default_profile: Boolean,
+ val default_profile_image: Boolean,
+ val description: String,
+ val favourites_count: Int,
+ val follow_request_sent: Boolean,
+ val followers_count: Int,
+ val following: Boolean,
+ val friends_count: Int,
+ val geo_enabled: Boolean,
+ val id: Int,
+ val id_str: String,
+ val is_translator: Boolean,
+ val lang: String,
+ val listed_count: Int,
+ val location: String,
+ val name: String,
+ val notifications: Boolean,
+ val profile_background_color: String,
+ val profile_background_image_url: String,
+ val profile_background_image_url_https: String,
+ val profile_background_tile: Boolean,
+ val profile_image_url: String,
+ val profile_image_url_https: String,
+ val profile_link_color: String,
+ val profile_sidebar_border_color: String,
+ val profile_sidebar_fill_color: String,
+ val profile_text_color: String,
+ val profile_use_background_image: Boolean,
+ val url: String? = null,
+ val `protected`: Boolean,
+ val screen_name: String,
+ val show_all_inline_media: Boolean,
+ val statuses_count: Int,
+ val time_zone: String?,
+ val utc_offset: Int?,
+ val verified: Boolean
+)
+
+@Serializable
+data class Hashtag(
+ val indices: List<Int>,
+ val text: String
+)
+
+@Serializable
+data class Media(
+ val display_url: String,
+ val expanded_url: String,
+ val id: Long,
+ val id_str: String,
+ val indices: List<Int>,
+ val media_url: String,
+ val media_url_https: String,
+ val sizes: Sizes,
+ val type: String,
+ val url: String
+)
+
+@Serializable
+data class Url(
+ val display_url: String,
+ val expanded_url: String,
+ val indices: List<Int>,
+ val url: String
+)
+
+@Serializable
+data class UserMention(
+ val id: Int,
+ val id_str: String,
+ val indices: List<Int>,
+ val name: String,
+ val screen_name: String
+)
+
+@Serializable
+data class Sizes(
+ val large: Large,
+ val medium: Medium,
+ val small: Small,
+ val thumb: Thumb
+)
+
+@Serializable
+data class Large(
+ val h: Int,
+ val resize: String,
+ val w: Int
+)
+
+@Serializable
+data class Medium(
+ val h: Int,
+ val resize: String,
+ val w: Int
+)
+
+@Serializable
+data class Small(
+ val h: Int,
+ val resize: String,
+ val w: Int
+)
+
+@Serializable
+data class Thumb(
+ val h: Int,
+ val resize: String,
+ val w: Int
+)
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoBaseline.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoBaseline.kt
new file mode 100644
index 00000000..06140e2a
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoBaseline.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.benchmarks.protobuf
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.protobuf.*
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@State(Scope.Benchmark)
+@Fork(1)
+open class ProtoBaseline {
+
+ @Serializable
+ class Holder(val a: Int, val b: Int, val c: Long, val d: Double)
+
+ @Serializable
+ class HolderExplicit(@ProtoNumber(1) val a: Int, @ProtoNumber(2) val b: Int, @ProtoNumber(3) val c: Long, @ProtoNumber(4) val d: Double)
+
+ private val holder = Holder(1, 2, 3L, 4.0)
+ private val holderBytes = ProtoBuf.encodeToByteArray(Holder.serializer(), holder)
+
+ private val holderExplicit = HolderExplicit(1, 2, 3L, 4.0)
+ private val holderHolderExplicitBytes = ProtoBuf.encodeToByteArray(HolderExplicit.serializer(), holderExplicit)
+
+ @Benchmark
+ fun toBytes() = ProtoBuf.encodeToByteArray(Holder.serializer(), holder)
+
+ @Benchmark
+ fun fromBytes() = ProtoBuf.decodeFromByteArray(Holder.serializer(), holderBytes)
+
+ @Benchmark
+ fun toBytesExplicit() = ProtoBuf.encodeToByteArray(HolderExplicit.serializer(), holderExplicit)
+
+ @Benchmark
+ fun fromBytesExplicit() = ProtoBuf.decodeFromByteArray(HolderExplicit.serializer(), holderHolderExplicitBytes)
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoHuge.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoHuge.kt
new file mode 100644
index 00000000..622d61b7
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoHuge.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.benchmarks.protobuf
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.protobuf.*
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@State(Scope.Benchmark)
+@Fork(1)
+open class ProtoHuge {
+
+ @Serializable
+ data class Values130(
+ val field0: Int,
+ val field1: Int,
+ val field2: Int,
+ val field3: Int,
+ val field4: Int,
+ val field5: Int,
+ val field6: Int,
+ val field7: Int,
+ val field8: Int,
+ val field9: Int,
+
+ val field10: Int,
+ val field11: Int,
+ val field12: Int,
+ val field13: Int,
+ val field14: Int,
+ val field15: Int,
+ val field16: Int,
+ val field17: Int,
+ val field18: Int,
+ val field19: Int,
+
+ val field20: Int,
+ val field21: Int,
+ val field22: Int,
+ val field23: Int,
+ val field24: Int,
+ val field25: Int,
+ val field26: Int,
+ val field27: Int,
+ val field28: Int,
+ val field29: Int,
+
+ val field30: Int,
+ val field31: Int,
+ val field32: Int,
+ val field33: Int,
+ val field34: Int,
+ val field35: Int,
+ val field36: Int,
+ val field37: Int,
+ val field38: Int,
+ val field39: Int,
+
+ val field40: Int,
+ val field41: Int,
+ val field42: Int,
+ val field43: Int,
+ val field44: Int,
+ val field45: Int,
+ val field46: Int,
+ val field47: Int,
+ val field48: Int,
+ val field49: Int,
+
+ val field50: Int,
+ val field51: Int,
+ val field52: Int,
+ val field53: Int,
+ val field54: Int,
+ val field55: Int,
+ val field56: Int,
+ val field57: Int,
+ val field58: Int,
+ val field59: Int,
+
+ val field60: Int,
+ val field61: Int,
+ val field62: Int,
+ val field63: Int,
+ val field64: Int,
+ val field65: Int,
+ val field66: Int,
+ val field67: Int,
+ val field68: Int,
+ val field69: Int,
+
+ val field70: Int,
+ val field71: Int,
+ val field72: Int,
+ val field73: Int,
+ val field74: Int,
+ val field75: Int,
+ val field76: Int,
+ val field77: Int,
+ val field78: Int,
+ val field79: Int,
+
+ val field80: Int,
+ val field81: Int,
+ val field82: Int,
+ val field83: Int,
+ val field84: Int,
+ val field85: Int,
+ val field86: Int,
+ val field87: Int,
+ val field88: Int,
+ val field89: Int,
+
+ val field90: Int,
+ val field91: Int,
+ val field92: Int,
+ val field93: Int,
+ val field94: Int,
+ val field95: Int,
+ val field96: Int,
+ val field97: Int,
+ val field98: Int,
+ val field99: Int,
+
+ val field100: Int,
+ val field101: Int,
+ val field102: Int,
+ val field103: Int,
+ val field104: Int,
+ val field105: Int,
+ val field106: Int,
+ val field107: Int,
+ val field108: Int,
+ val field109: Int,
+
+ val field110: Int,
+ val field111: Int,
+ val field112: Int,
+ val field113: Int,
+ val field114: Int,
+ val field115: Int,
+ val field116: Int,
+ val field117: Int,
+ val field118: Int,
+ val field119: Int,
+
+ val field120: Int,
+ val field121: Int,
+ val field122: Int,
+ val field123: Int,
+ val field124: Int,
+ val field125: Int,
+ val field126: Int,
+ val field127: Int,
+ val field128: Int,
+ val field129: Int
+ )
+
+ private val values130 = Values130(
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129
+ )
+
+ private val values130Bytes = ProtoBuf.encodeToByteArray(Values130.serializer(), values130)
+
+ @Benchmark
+ fun toBytes130() = ProtoBuf.encodeToByteArray(Values130.serializer(), values130)
+
+ @Benchmark
+ fun fromBytes130() = ProtoBuf.decodeFromByteArray(Values130.serializer(), values130Bytes)
+
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoListBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoListBenchmark.kt
new file mode 100644
index 00000000..4b24b09f
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoListBenchmark.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.benchmarks.protobuf
+
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+import kotlinx.serialization.protobuf.ProtoBuf.Default.encodeToByteArray
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@State(Scope.Benchmark)
+@Fork(1)
+open class ProtoListBenchmark {
+
+ @Serializable
+ class Holder(val a: Int, val b: Int, val c: Long, val d: Double)
+
+ @Serializable
+ class HolderList(val list: List<Holder>)
+
+ private val h = Holder(1, 2, 3L, 4.0)
+ private val value = HolderList(listOf(h, h, h, h, h))
+ private val bytes = ProtoBuf.encodeToByteArray(value)
+
+ @Benchmark
+ fun toBytes() = ProtoBuf.encodeToByteArray(HolderList.serializer(), value)
+
+ @Benchmark
+ fun fromBytes() = ProtoBuf.decodeFromByteArray(HolderList.serializer(), bytes)
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoListLikeBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoListLikeBenchmark.kt
new file mode 100644
index 00000000..af1699dd
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoListLikeBenchmark.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.benchmarks.protobuf
+
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@State(Scope.Benchmark)
+@Fork(1)
+open class ProtoListLikeBenchmark {
+
+ @Serializable
+ class Holder(val a: Int, val b: Int, val c: Long, val d: Double)
+
+ @Serializable
+ class HolderList(val h1: Holder, val h2: Holder, val h3: Holder, val h4: Holder, val h5: Holder)
+
+ private val h = Holder(1, 2, 3L, 4.0)
+ private val value = HolderList(h, h, h, h, h)
+ private val bytes = ProtoBuf.encodeToByteArray(value)
+
+ @Benchmark
+ fun toBytes() = ProtoBuf.encodeToByteArray(HolderList.serializer(), value)
+
+ @Benchmark
+ fun fromBytes() = ProtoBuf.decodeFromByteArray(HolderList.serializer(), bytes)
+}
diff --git a/benchmark/src/jmh/resources/citm_catalog.json b/benchmark/src/jmh/resources/citm_catalog.json
new file mode 100644
index 00000000..245fdbbe
--- /dev/null
+++ b/benchmark/src/jmh/resources/citm_catalog.json
@@ -0,0 +1,50469 @@
+{
+ "areaNames": {
+ "205705993": "Arrière-scène central",
+ "205705994": "1er balcon central",
+ "205705995": "2ème balcon bergerie cour",
+ "205705996": "2ème balcon bergerie jardin",
+ "205705998": "1er balcon bergerie jardin",
+ "205705999": "1er balcon bergerie cour",
+ "205706000": "Arrière-scène jardin",
+ "205706001": "Arrière-scène cour",
+ "205706002": "2ème balcon jardin",
+ "205706003": "2ème balcon cour",
+ "205706004": "2ème Balcon central",
+ "205706005": "1er balcon jardin",
+ "205706006": "1er balcon cour",
+ "205706007": "Orchestre central",
+ "205706008": "Orchestre jardin",
+ "205706009": "Orchestre cour",
+ "342752287": "Zone physique secrète"
+ },
+ "audienceSubCategoryNames": {
+ "337100890": "Abonné"
+ },
+ "blockNames": {},
+ "events": {
+ "138586341": {
+ "description": null,
+ "id": 138586341,
+ "logo": null,
+ "name": "30th Anniversary Tour",
+ "subTopicIds": [
+ 337184269,
+ 337184283
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604
+ ]
+ },
+ "138586345": {
+ "description": null,
+ "id": 138586345,
+ "logo": "/images/UE0AAAAACEKo6QAAAAZDSVRN",
+ "name": "Berliner Philharmoniker",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586349": {
+ "description": null,
+ "id": 138586349,
+ "logo": "/images/UE0AAAAACEKo7QAAAAZDSVRN",
+ "name": "Berliner Philharmoniker",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586353": {
+ "description": null,
+ "id": 138586353,
+ "logo": "/images/UE0AAAAACEKo8QAAAAZDSVRN",
+ "name": "Pittsburgh Symphony Orchestra",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586357": {
+ "description": null,
+ "id": 138586357,
+ "logo": "/images/UE0AAAAACEKo9QAAAAhDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586361": {
+ "description": null,
+ "id": 138586361,
+ "logo": "/images/UE0AAAAACEKo+QAAAAVDSVRN",
+ "name": "WDR Sinfonieorchester Köln",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586365": {
+ "description": null,
+ "id": 138586365,
+ "logo": "/images/UE0AAAAACEKo/QAAAAVDSVRN",
+ "name": "Alessandro - G.F. Haendel",
+ "subTopicIds": [
+ 337184284,
+ 337184263,
+ 337184298,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586369": {
+ "description": null,
+ "id": 138586369,
+ "logo": "/images/UE0AAAAACEKpAQAAAAVDSVRN",
+ "name": "Orchestre Colonne",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586373": {
+ "description": null,
+ "id": 138586373,
+ "logo": "/images/UE0AAAAACEKpBQAAAAdDSVRN",
+ "name": "Christophe",
+ "subTopicIds": [
+ 337184280,
+ 337184297,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586377": {
+ "description": null,
+ "id": 138586377,
+ "logo": "/images/UE0AAAAACEKpCQAAAAVDSVRN",
+ "name": "Joshua Redman Quartet",
+ "subTopicIds": [
+ 337184269,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586381": {
+ "description": null,
+ "id": 138586381,
+ "logo": "/images/UE0AAAAACEKpDQAAAAVDSVRN",
+ "name": "Orchestre Symphonique d'Etat de São Paulo",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586385": {
+ "description": null,
+ "id": 138586385,
+ "logo": "/images/UE0AAAAACEKpEQAAAAVDSVRN",
+ "name": "Le génie italien",
+ "subTopicIds": [
+ 337184284,
+ 337184298,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586389": {
+ "description": null,
+ "id": 138586389,
+ "logo": "/images/UE0AAAAACEKpFQAAAAVDSVRN",
+ "name": "Les Noces de Figaro - W.A. Mozart (version de concert)",
+ "subTopicIds": [
+ 337184284,
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586393": {
+ "description": null,
+ "id": 138586393,
+ "logo": "/images/UE0AAAAACEKpGQAAAAhDSVRN",
+ "name": "Orchestre Pasdeloup",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586397": {
+ "description": null,
+ "id": 138586397,
+ "logo": null,
+ "name": "The Saxophone Summit",
+ "subTopicIds": [
+ 337184269,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586401": {
+ "description": null,
+ "id": 138586401,
+ "logo": "/images/UE0AAAAACEKpIQAAAAVDSVRN",
+ "name": "Patricia Petibon - Nouveau Monde",
+ "subTopicIds": [
+ 337184263,
+ 337184298,
+ 337184283,
+ 337184292
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586405": {
+ "description": null,
+ "id": 138586405,
+ "logo": "/images/UE0AAAAACEKpJQAAAAVDSVRN",
+ "name": "Russian National Orchestra",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586409": {
+ "description": null,
+ "id": 138586409,
+ "logo": "/images/UE0AAAAACEKpKQAAAAZDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586413": {
+ "description": null,
+ "id": 138586413,
+ "logo": "/images/UE0AAAAACEKpLQAAAAVDSVRN",
+ "name": "Evgeny Kissin",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586417": {
+ "description": null,
+ "id": 138586417,
+ "logo": "/images/UE0AAAAACEKpMQAAAAZDSVRN",
+ "name": "Bach, concertos pour piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586421": {
+ "description": null,
+ "id": 138586421,
+ "logo": "/images/UE0AAAAACEKpNQAAAAVDSVRN",
+ "name": "Bach, concertos pour piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586425": {
+ "description": null,
+ "id": 138586425,
+ "logo": null,
+ "name": "Orchestre National d'ÃŽle-de-France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586429": {
+ "description": null,
+ "id": 138586429,
+ "logo": "/images/UE0AAAAACEKpPQAAAAVDSVRN",
+ "name": "Gewandhausorchester Leipzig",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586433": {
+ "description": null,
+ "id": 138586433,
+ "logo": "/images/UE0AAAAACEKpQQAAAAVDSVRN",
+ "name": "Gewandhausorchester Leipzig",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586437": {
+ "description": null,
+ "id": 138586437,
+ "logo": "/images/UE0AAAAACEKpRQAAAAVDSVRN",
+ "name": "Budapest Festival Orchestra",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586441": {
+ "description": null,
+ "id": 138586441,
+ "logo": "/images/UE0AAAAACEKpSQAAAAVDSVRN",
+ "name": "Orchestre National du Capitole de Toulouse",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586445": {
+ "description": null,
+ "id": 138586445,
+ "logo": "/images/UE0AAAAACEKpTQAAAAVDSVRN",
+ "name": "Gewandhausorchester Leipzig",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586449": {
+ "description": null,
+ "id": 138586449,
+ "logo": "/images/UE0AAAAACEKpUQAAAAVDSVRN",
+ "name": "Gewandhausorchester Leipzig",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586453": {
+ "description": null,
+ "id": 138586453,
+ "logo": "/images/UE0AAAAACEKpVQAAAAVDSVRN",
+ "name": "Remember Shakti",
+ "subTopicIds": [
+ 337184269,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586457": {
+ "description": null,
+ "id": 138586457,
+ "logo": "/images/UE0AAAAACEKpWQAAAAVDSVRN",
+ "name": "Menahem Pressler - Quatuor Ebène",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586461": {
+ "description": null,
+ "id": 138586461,
+ "logo": "/images/UE0AAAAACEKpXQAAAAZDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586465": {
+ "description": null,
+ "id": 138586465,
+ "logo": "/images/UE0AAAAACEKpYQAAAAVDSVRN",
+ "name": "Orquesta Buena Vista Social Club",
+ "subTopicIds": [
+ 337184279,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586469": {
+ "description": null,
+ "id": 138586469,
+ "logo": "/images/UE0AAAAACEKpZQAAAAVDSVRN",
+ "name": "The Cleveland Orchestra",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586473": {
+ "description": null,
+ "id": 138586473,
+ "logo": "/images/UE0AAAAACEKpaQAAAAVDSVRN",
+ "name": "The Cleveland Orchestra",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586477": {
+ "description": null,
+ "id": 138586477,
+ "logo": "/images/UE0AAAAACEKpbQAAAAZDSVRN",
+ "name": "Orchestre Philharmonique du Luxembourg",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586481": {
+ "description": null,
+ "id": 138586481,
+ "logo": "/images/UE0AAAAACEKpcQAAAAVDSVRN",
+ "name": "Maurizio Pollini, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586485": {
+ "description": null,
+ "id": 138586485,
+ "logo": "/images/UE0AAAAACEKpdQAAAAZDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586501": {
+ "description": null,
+ "id": 138586501,
+ "logo": "/images/UE0AAAAACEKphQAAAAVDSVRN",
+ "name": "Antonio Meneses - Maria-João Pires",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586505": {
+ "description": null,
+ "id": 138586505,
+ "logo": "/images/UE0AAAAACEKpiQAAAAVDSVRN",
+ "name": "Musiques pour la reine Caroline",
+ "subTopicIds": [
+ 337184284,
+ 337184263,
+ 337184298,
+ 337184283,
+ 337184292
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586509": {
+ "description": null,
+ "id": 138586509,
+ "logo": null,
+ "name": "Orchestre National d'ÃŽle-de-France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586513": {
+ "description": null,
+ "id": 138586513,
+ "logo": "/images/UE0AAAAACEKpkQAAAAVDSVRN",
+ "name": "Les Mystères d'Isis - W.A. Mozart (cersion de concert)",
+ "subTopicIds": [
+ 337184284,
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586517": {
+ "description": null,
+ "id": 138586517,
+ "logo": "/images/UE0AAAAACEKplQAAAAdDSVRN",
+ "name": "Martha Argerich - Gidon Kremer",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586521": {
+ "description": null,
+ "id": 138586521,
+ "logo": "/images/UE0AAAAACEKpmQAAAAVDSVRN",
+ "name": "Cecilia Bartoli - Mozart et la Vienne classique",
+ "subTopicIds": [
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184292
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586525": {
+ "description": null,
+ "id": 138586525,
+ "logo": "/images/UE0AAAAACEKpnQAAAAVDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586529": {
+ "description": null,
+ "id": 138586529,
+ "logo": null,
+ "name": "Orchestre Pasdeloup",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586533": {
+ "description": null,
+ "id": 138586533,
+ "logo": "/images/UE0AAAAACEKppQAAAAVDSVRN",
+ "name": "Orchestre du Théâtre Mariinsky",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586537": {
+ "description": null,
+ "id": 138586537,
+ "logo": "/images/UE0AAAAACEKpqQAAAAVDSVRN",
+ "name": "Orchestre du Théâtre Mariinsky",
+ "subTopicIds": [
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586541": {
+ "description": null,
+ "id": 138586541,
+ "logo": "/images/UE0AAAAACEKprQAAAAVDSVRN",
+ "name": "Orchestre du Théâtre Mariinsky",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586545": {
+ "description": null,
+ "id": 138586545,
+ "logo": "/images/UE0AAAAACEKpsQAAAAVDSVRN",
+ "name": "Academy of Saint Martin in the Fields",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586549": {
+ "description": null,
+ "id": 138586549,
+ "logo": "/images/UE0AAAAACEKptQAAAAVDSVRN",
+ "name": "Quatuor Hagen",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586553": {
+ "description": null,
+ "id": 138586553,
+ "logo": "/images/UE0AAAAACEKpuQAAAAVDSVRN",
+ "name": "Quatuor Hagen",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586557": {
+ "description": null,
+ "id": 138586557,
+ "logo": "/images/UE0AAAAACEKpvQAAAAVDSVRN",
+ "name": "Quatuor Hagen",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586561": {
+ "description": null,
+ "id": 138586561,
+ "logo": "/images/UE0AAAAACEKpwQAAAAVDSVRN",
+ "name": "Sunwook Kim, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586565": {
+ "description": null,
+ "id": 138586565,
+ "logo": null,
+ "name": "Orchestre Colonne",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586569": {
+ "description": null,
+ "id": 138586569,
+ "logo": "/images/UE0AAAAACEKpyQAAAAVDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586581": {
+ "description": null,
+ "id": 138586581,
+ "logo": null,
+ "name": "Orchestre National de France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586585": {
+ "description": null,
+ "id": 138586585,
+ "logo": "/images/UE0AAAAACEKp2QAAAAVDSVRN",
+ "name": "Messe en si mineur - J.S. Bach",
+ "subTopicIds": [
+ 337184296,
+ 337184263,
+ 337184298,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586589": {
+ "description": null,
+ "id": 138586589,
+ "logo": null,
+ "name": "Le Messie - G.F. Haendel",
+ "subTopicIds": [
+ 337184263,
+ 337184298,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586593": {
+ "description": null,
+ "id": 138586593,
+ "logo": "/images/UE0AAAAACEKp4QAAAAdDSVRN",
+ "name": "Orchestre National d'ÃŽle-de-France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586597": {
+ "description": null,
+ "id": 138586597,
+ "logo": "/images/UE0AAAAACEKp5QAAAAVDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586601": {
+ "description": null,
+ "id": 138586601,
+ "logo": "/images/UE0AAAAACEKp6QAAAAdDSVRN",
+ "name": "Orchestre Pasdeloup",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586605": {
+ "description": null,
+ "id": 138586605,
+ "logo": null,
+ "name": "Orchestre Colonne",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586609": {
+ "description": null,
+ "id": 138586609,
+ "logo": null,
+ "name": "Ciné-concert - Le Cuirassé Potemkine",
+ "subTopicIds": [
+ 337184267,
+ 337184262,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586613": {
+ "description": null,
+ "id": 138586613,
+ "logo": "/images/UE0AAAAACEKp9QAAAAVDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586617": {
+ "description": null,
+ "id": 138586617,
+ "logo": "/images/UE0AAAAACEKp+QAAAAVDSVRN",
+ "name": "London Symphony Orchestra",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586625": {
+ "description": null,
+ "id": 138586625,
+ "logo": null,
+ "name": "Orchestre National d'ÃŽle-de-France",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586629": {
+ "description": null,
+ "id": 138586629,
+ "logo": "/images/UE0AAAAACEKqBQAAAAVDSVRN",
+ "name": "Orquesta Sinfonica Simón Bolívar de Venezuela",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586633": {
+ "description": null,
+ "id": 138586633,
+ "logo": "/images/UE0AAAAACEKqCQAAAAVDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184298,
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586641": {
+ "description": null,
+ "id": 138586641,
+ "logo": "/images/UE0AAAAACEKqEQAAAAVDSVRN",
+ "name": "Edita Gruberova - Airs de concert",
+ "subTopicIds": [
+ 337184284,
+ 337184298,
+ 337184283,
+ 337184292
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586645": {
+ "description": null,
+ "id": 138586645,
+ "logo": "/images/UE0AAAAACEKqFQAAAAdDSVRN",
+ "name": "Orchestre National d'ÃŽle-de-France",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586649": {
+ "description": null,
+ "id": 138586649,
+ "logo": "/images/UE0AAAAACEKqGQAAAAZDSVRN",
+ "name": "Alexei Volodin, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586653": {
+ "description": null,
+ "id": 138586653,
+ "logo": null,
+ "name": "Sonya Yoncheva - Diva !",
+ "subTopicIds": [
+ 337184284,
+ 337184263,
+ 337184298,
+ 337184283,
+ 337184292
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586657": {
+ "description": null,
+ "id": 138586657,
+ "logo": "/images/UE0AAAAACEKqIQAAAAVDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586661": {
+ "description": null,
+ "id": 138586661,
+ "logo": null,
+ "name": "Le Ramayana balinais - L'Enlèvement de Sita",
+ "subTopicIds": [
+ 337184279,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586667": {
+ "description": null,
+ "id": 138586667,
+ "logo": null,
+ "name": "Dave Holland & friends",
+ "subTopicIds": [
+ 337184269,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586671": {
+ "description": null,
+ "id": 138586671,
+ "logo": "/images/UE0AAAAACEKqLwAAAAlDSVRN",
+ "name": "Boris Godounov - M.Moussorgski (version de concert)",
+ "subTopicIds": [
+ 337184284,
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586675": {
+ "description": null,
+ "id": 138586675,
+ "logo": "/images/UE0AAAAACEKqMwAAAAVDSVRN",
+ "name": "Insula orchestra - Accentus",
+ "subTopicIds": [
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586679": {
+ "description": null,
+ "id": 138586679,
+ "logo": "/images/UE0AAAAACEKqNwAAAAVDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586687": {
+ "description": null,
+ "id": 138586687,
+ "logo": "/images/UE0AAAAACEKqPwAAAAVDSVRN",
+ "name": "Bryn Terfel - Héros légendaires",
+ "subTopicIds": [
+ 337184284,
+ 337184298,
+ 337184283,
+ 337184292
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586691": {
+ "description": null,
+ "id": 138586691,
+ "logo": null,
+ "name": "Les Siècles",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586695": {
+ "description": null,
+ "id": 138586695,
+ "logo": "/images/UE0AAAAACEKqRwAAAAVDSVRN",
+ "name": "Gautier Capuçon - Frank Braley",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586699": {
+ "description": null,
+ "id": 138586699,
+ "logo": null,
+ "name": "Festival Présences 2014 \"Paris Berlin\"",
+ "subTopicIds": [
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586703": {
+ "description": null,
+ "id": 138586703,
+ "logo": "/images/UE0AAAAACEKqTwAAAAZDSVRN",
+ "name": "Autour de Tristan",
+ "subTopicIds": [
+ 337184284,
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586707": {
+ "description": null,
+ "id": 138586707,
+ "logo": "/images/UE0AAAAACEKqUwAAAAVDSVRN",
+ "name": "Orchestre du Théâtre Mariinsky",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586711": {
+ "description": null,
+ "id": 138586711,
+ "logo": "/images/UE0AAAAACEKqVwAAAAVDSVRN",
+ "name": "Orchestre du Théâtre Mariinsky",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586715": {
+ "description": null,
+ "id": 138586715,
+ "logo": "/images/UE0AAAAACEKqWwAAAAVDSVRN",
+ "name": "Orchestre du Théâtre Mariinsky",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586719": {
+ "description": null,
+ "id": 138586719,
+ "logo": "/images/UE0AAAAACEKqXwAAAAVDSVRN",
+ "name": "Etienne Daho et invités",
+ "subTopicIds": [
+ 337184280,
+ 337184297,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586723": {
+ "description": null,
+ "id": 138586723,
+ "logo": null,
+ "name": "Fantasia in concert",
+ "subTopicIds": [
+ 337184299,
+ 337184268,
+ 337184267,
+ 337184275,
+ 337184282
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846098,
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586731": {
+ "description": null,
+ "id": 138586731,
+ "logo": "/images/UE0AAAAACEKqawAAAAVDSVRN",
+ "name": "Khatia Buniatishvili, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586735": {
+ "description": null,
+ "id": 138586735,
+ "logo": "/images/UE0AAAAACEKqbwAAAAVDSVRN",
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586743": {
+ "description": null,
+ "id": 138586743,
+ "logo": null,
+ "name": "Guy Braunstein - Zvi Plesser - Sunwook Kim",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586747": {
+ "description": null,
+ "id": 138586747,
+ "logo": "/images/UE0AAAAACEKqewAAAAVDSVRN",
+ "name": "Janine Jansen and friends",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586751": {
+ "description": null,
+ "id": 138586751,
+ "logo": "/images/UE0AAAAACEKqfwAAAAVDSVRN",
+ "name": "Elena Bashkirova, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586755": {
+ "description": null,
+ "id": 138586755,
+ "logo": null,
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184284,
+ 337184298,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586759": {
+ "description": null,
+ "id": 138586759,
+ "logo": null,
+ "name": "San Francisco Symphony",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586771": {
+ "description": null,
+ "id": 138586771,
+ "logo": null,
+ "name": "Passion selon saint Jean - J.S. Bach",
+ "subTopicIds": [
+ 337184296,
+ 337184263,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586775": {
+ "description": null,
+ "id": 138586775,
+ "logo": null,
+ "name": "Yundi Li , piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586779": {
+ "description": null,
+ "id": 138586779,
+ "logo": null,
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586783": {
+ "description": null,
+ "id": 138586783,
+ "logo": null,
+ "name": "Orchestre Pasdeloup",
+ "subTopicIds": [
+ 337184268,
+ 337184269,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586787": {
+ "description": null,
+ "id": 138586787,
+ "logo": null,
+ "name": "Orchestre du Conservatoire de Paris",
+ "subTopicIds": [
+ 337184284,
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586795": {
+ "description": null,
+ "id": 138586795,
+ "logo": null,
+ "name": "Orchestre National d'ÃŽle-de-France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586799": {
+ "description": null,
+ "id": 138586799,
+ "logo": null,
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586803": {
+ "description": null,
+ "id": 138586803,
+ "logo": null,
+ "name": "Royal Concertgebouw Orchestra Amsterdam",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586807": {
+ "description": null,
+ "id": 138586807,
+ "logo": null,
+ "name": "Royal Concertgebouw Orchestra Amsterdam",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586811": {
+ "description": null,
+ "id": 138586811,
+ "logo": null,
+ "name": "Royal Concertgebouw Orchestra Amsterdam",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586815": {
+ "description": null,
+ "id": 138586815,
+ "logo": null,
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586823": {
+ "description": null,
+ "id": 138586823,
+ "logo": null,
+ "name": "London Symphony Orchestra",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586827": {
+ "description": null,
+ "id": 138586827,
+ "logo": null,
+ "name": "London Symphony Orchestra",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586831": {
+ "description": null,
+ "id": 138586831,
+ "logo": null,
+ "name": "Le Concert des Nations - Jordi Savall",
+ "subTopicIds": [
+ 337184263,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586835": {
+ "description": null,
+ "id": 138586835,
+ "logo": null,
+ "name": "Leonidas Kavakos - Yuja Wang",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586839": {
+ "description": null,
+ "id": 138586839,
+ "logo": null,
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586843": {
+ "description": null,
+ "id": 138586843,
+ "logo": null,
+ "name": "Quatuor Artemis - Gautier Capuçon",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586847": {
+ "description": null,
+ "id": 138586847,
+ "logo": null,
+ "name": "Quatuor Artemis - Quatuor Ébène",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586851": {
+ "description": null,
+ "id": 138586851,
+ "logo": null,
+ "name": "Quatuor Artemis - Elisabeth Leonskaja",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586855": {
+ "description": null,
+ "id": 138586855,
+ "logo": null,
+ "name": "Russian National Orchestra",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586859": {
+ "description": null,
+ "id": 138586859,
+ "logo": null,
+ "name": "Passion selon saint Matthieu",
+ "subTopicIds": [
+ 337184296,
+ 337184263,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586863": {
+ "description": null,
+ "id": 138586863,
+ "logo": null,
+ "name": "Les Arts Florissants - Concert de Pâques",
+ "subTopicIds": [
+ 337184263,
+ 337184298,
+ 337184283,
+ 337184292
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586867": {
+ "description": null,
+ "id": 138586867,
+ "logo": null,
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586871": {
+ "description": null,
+ "id": 138586871,
+ "logo": null,
+ "name": "Leylâ et Majnûn ou L'Amour mystique",
+ "subTopicIds": [
+ 337184279,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586875": {
+ "description": null,
+ "id": 138586875,
+ "logo": null,
+ "name": "Stephen Kovacevich, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586879": {
+ "description": null,
+ "id": 138586879,
+ "logo": null,
+ "name": "Orchestra Mozart Bologna - Mahler Chamber Orchestra",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586883": {
+ "description": null,
+ "id": 138586883,
+ "logo": null,
+ "name": "Ballet Royal du Cambodge",
+ "subTopicIds": [
+ 337184279,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586889": {
+ "description": null,
+ "id": 138586889,
+ "logo": null,
+ "name": "MDR Sinfonieorchester Leipzig",
+ "subTopicIds": [
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586893": {
+ "description": null,
+ "id": 138586893,
+ "logo": null,
+ "name": "Orchestre Colonne",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586897": {
+ "description": null,
+ "id": 138586897,
+ "logo": null,
+ "name": "Elisabeth Leonskaja, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586901": {
+ "description": null,
+ "id": 138586901,
+ "logo": null,
+ "name": "Yuja Wang, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586905": {
+ "description": null,
+ "id": 138586905,
+ "logo": null,
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586909": {
+ "description": null,
+ "id": 138586909,
+ "logo": null,
+ "name": "Anne-Sophie Mutter - Lambert Orkis",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586917": {
+ "description": null,
+ "id": 138586917,
+ "logo": null,
+ "name": "Orchestre National d'ÃŽle-de-France",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586921": {
+ "description": null,
+ "id": 138586921,
+ "logo": null,
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586929": {
+ "description": null,
+ "id": 138586929,
+ "logo": null,
+ "name": "Orchestre Pasdeloup",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586933": {
+ "description": null,
+ "id": 138586933,
+ "logo": null,
+ "name": "Gilberto Gil",
+ "subTopicIds": [
+ 337184279,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586937": {
+ "description": null,
+ "id": 138586937,
+ "logo": null,
+ "name": "Nelson Freire, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586941": {
+ "description": null,
+ "id": 138586941,
+ "logo": null,
+ "name": "Orchestre Philharmonique de Radio France",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586945": {
+ "description": null,
+ "id": 138586945,
+ "logo": null,
+ "name": "Orfeo - C. Monteverdi (version de concert)",
+ "subTopicIds": [
+ 337184284,
+ 337184263,
+ 337184298,
+ 337184283,
+ 337184292
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586949": {
+ "description": null,
+ "id": 138586949,
+ "logo": null,
+ "name": "Bamberger Symphoniker",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586953": {
+ "description": null,
+ "id": 138586953,
+ "logo": null,
+ "name": "Murray Perahia, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586957": {
+ "description": null,
+ "id": 138586957,
+ "logo": null,
+ "name": "Orchestre National du Capitole de Toulouse",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586961": {
+ "description": null,
+ "id": 138586961,
+ "logo": null,
+ "name": "Krystian Zimerman, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586965": {
+ "description": null,
+ "id": 138586965,
+ "logo": null,
+ "name": "Rafal Blechacz, piano",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586969": {
+ "description": null,
+ "id": 138586969,
+ "logo": null,
+ "name": "Les Voyages musicaux de Marco Polo",
+ "subTopicIds": [
+ 337184279,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586973": {
+ "description": null,
+ "id": 138586973,
+ "logo": null,
+ "name": "Orchestre National de Lyon",
+ "subTopicIds": [
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184292,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586977": {
+ "description": null,
+ "id": 138586977,
+ "logo": null,
+ "name": "Guy Braunstein - Zvi Plesser - Sunwook Kim",
+ "subTopicIds": [
+ 337184281,
+ 337184283,
+ 337184273
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586981": {
+ "description": null,
+ "id": 138586981,
+ "logo": null,
+ "name": "La Bohème - G. Puccini (version de concert)",
+ "subTopicIds": [
+ 337184284,
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586989": {
+ "description": null,
+ "id": 138586989,
+ "logo": null,
+ "name": "Otello - G. Verdi (version de concert)",
+ "subTopicIds": [
+ 337184284,
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184292
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586993": {
+ "description": null,
+ "id": 138586993,
+ "logo": null,
+ "name": "Staatskapelle Berlin",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "138586997": {
+ "description": null,
+ "id": 138586997,
+ "logo": null,
+ "name": "Staatskapelle Berlin",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "151183114": {
+ "description": null,
+ "id": 151183114,
+ "logo": null,
+ "name": "San Francisco Symphony",
+ "subTopicIds": [
+ 337184298,
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "339420802": {
+ "description": null,
+ "id": 339420802,
+ "logo": null,
+ "name": "Lou Doillon",
+ "subTopicIds": [
+ 337184280,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "339420805": {
+ "description": null,
+ "id": 339420805,
+ "logo": null,
+ "name": "Patrick Watson & Orchestre National d'Ile-de-France",
+ "subTopicIds": [
+ 337184280,
+ 337184283,
+ 337184262
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341069930": {
+ "description": null,
+ "id": 341069930,
+ "logo": "/images/UE0AAAAAFFRQagAAAAlDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181232": {
+ "description": null,
+ "id": 341181232,
+ "logo": null,
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181233": {
+ "description": null,
+ "id": 341181233,
+ "logo": "/images/UE0AAAAAFFYDMQAAAAhDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181234": {
+ "description": null,
+ "id": 341181234,
+ "logo": "/images/UE0AAAAAFFYDMgAAAAdDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181235": {
+ "description": null,
+ "id": 341181235,
+ "logo": "/images/UE0AAAAAFFYDMwAAAAZDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181236": {
+ "description": null,
+ "id": 341181236,
+ "logo": "/images/UE0AAAAAFFYDNAAAAAZDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181237": {
+ "description": null,
+ "id": 341181237,
+ "logo": "/images/UE0AAAAAFFYDNQAAAAhDSVRN",
+ "name": "Paavo Järvi, direction",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181238": {
+ "description": null,
+ "id": 341181238,
+ "logo": "/images/UE0AAAAAFFYDNgAAAAdDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181239": {
+ "description": null,
+ "id": 341181239,
+ "logo": "/images/UE0AAAAAFFYDNwAAAAdDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181240": {
+ "description": null,
+ "id": 341181240,
+ "logo": "/images/UE0AAAAAFFYDOAAAAAhDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181241": {
+ "description": null,
+ "id": 341181241,
+ "logo": "/images/UE0AAAAAFFYDOQAAAAZDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181242": {
+ "description": null,
+ "id": 341181242,
+ "logo": "/images/UE0AAAAAFFYDOgAAAAdDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181243": {
+ "description": null,
+ "id": 341181243,
+ "logo": "/images/UE0AAAAAFFYDOwAAAAdDSVRN",
+ "name": "Concert anniversaire des 90 ans de Menahem Pressler",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181244": {
+ "description": null,
+ "id": 341181244,
+ "logo": "/images/UE0AAAAAFFYDPAAAAAZDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181245": {
+ "description": null,
+ "id": 341181245,
+ "logo": "/images/UE0AAAAAFFYDPQAAAAZDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181246": {
+ "description": null,
+ "id": 341181246,
+ "logo": null,
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181247": {
+ "description": null,
+ "id": 341181247,
+ "logo": null,
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181248": {
+ "description": null,
+ "id": 341181248,
+ "logo": "/images/UE0AAAAAFFYDQAAAAAZDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181249": {
+ "description": null,
+ "id": 341181249,
+ "logo": "/images/UE0AAAAAFFYDQQAAAAdDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181250": {
+ "description": null,
+ "id": 341181250,
+ "logo": "/images/UE0AAAAAFFYDQgAAAAdDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181251": {
+ "description": null,
+ "id": 341181251,
+ "logo": null,
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181252": {
+ "description": null,
+ "id": 341181252,
+ "logo": "/images/UE0AAAAAFFYDRAAAAAdDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181253": {
+ "description": null,
+ "id": 341181253,
+ "logo": null,
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181254": {
+ "description": null,
+ "id": 341181254,
+ "logo": "/images/UE0AAAAAFFYDRgAAAAlDSVRN",
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181255": {
+ "description": null,
+ "id": 341181255,
+ "logo": null,
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181256": {
+ "description": null,
+ "id": 341181256,
+ "logo": null,
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181257": {
+ "description": null,
+ "id": 341181257,
+ "logo": null,
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181258": {
+ "description": null,
+ "id": 341181258,
+ "logo": null,
+ "name": "Orchestre de Paris",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "341181259": {
+ "description": null,
+ "id": 341181259,
+ "logo": null,
+ "name": "14052122 JARVI / GOERNE / SOLBERG / CHÅ’UR",
+ "subTopicIds": [
+ 337184268,
+ 337184288,
+ 337184283,
+ 337184275
+ ],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": [
+ 324846099,
+ 107888604,
+ 324846100
+ ]
+ },
+ "342742592": {
+ "description": null,
+ "id": 342742592,
+ "logo": null,
+ "name": "event secret 2",
+ "subTopicIds": [],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": []
+ },
+ "342742593": {
+ "description": null,
+ "id": 342742593,
+ "logo": null,
+ "name": "event secret 3",
+ "subTopicIds": [],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": []
+ },
+ "342742594": {
+ "description": null,
+ "id": 342742594,
+ "logo": null,
+ "name": "event secret 4",
+ "subTopicIds": [],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": []
+ },
+ "342742595": {
+ "description": null,
+ "id": 342742595,
+ "logo": null,
+ "name": "event secret 5",
+ "subTopicIds": [],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": []
+ },
+ "342742596": {
+ "description": null,
+ "id": 342742596,
+ "logo": null,
+ "name": "event secret 6",
+ "subTopicIds": [],
+ "subjectCode": null,
+ "subtitle": null,
+ "topicIds": []
+ }
+ },
+ "performances": [
+ {
+ "eventId": 138586341,
+ "id": 339887544,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 90250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937295
+ },
+ {
+ "amount": 66500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937296
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937295
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937296
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1372701600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 339420802,
+ "id": 339430296,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937295
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937296
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937295
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937296
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1372788000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 339420805,
+ "id": 339430301,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937295
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937296
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937295
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937296
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1373220000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586345,
+ "id": 138586347,
+ "logo": "/images/UE0AAAAACEKo6QAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 152000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 104500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1377972000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586349,
+ "id": 138586351,
+ "logo": "/images/UE0AAAAACEKo7QAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 152000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 104500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1378044000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586353,
+ "id": 138586355,
+ "logo": "/images/UE0AAAAACEKo8QAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 90250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 71250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1378490400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341069930,
+ "id": 341070133,
+ "logo": "/images/UE0AAAAAFFRQagAAAAlDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1378922400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341069930,
+ "id": 341070132,
+ "logo": "/images/UE0AAAAAFFRQagAAAAlDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1379008800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586357,
+ "id": 138586359,
+ "logo": "/images/UE0AAAAACEKo9QAAAAhDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1379095200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586361,
+ "id": 138586363,
+ "logo": "/images/UE0AAAAACEKo+QAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1379440800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586365,
+ "id": 138586367,
+ "logo": "/images/UE0AAAAACEKo/QAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1379959200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181254,
+ "id": 341181470,
+ "logo": "/images/UE0AAAAAFFYDRgAAAAlDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1380132000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181254,
+ "id": 341181469,
+ "logo": "/images/UE0AAAAAFFYDRgAAAAlDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1380218400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586369,
+ "id": 138586371,
+ "logo": "/images/UE0AAAAACEKpAQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1380650400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181252,
+ "id": 341181467,
+ "logo": "/images/UE0AAAAAFFYDRAAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1380736800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586373,
+ "id": 138586375,
+ "logo": "/images/UE0AAAAACEKpBQAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1380996000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586377,
+ "id": 138586379,
+ "logo": "/images/UE0AAAAACEKpCQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1381082400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586381,
+ "id": 138586383,
+ "logo": "/images/UE0AAAAACEKpDQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1381168800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586385,
+ "id": 138586387,
+ "logo": "/images/UE0AAAAACEKpEQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1381255200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181234,
+ "id": 341181437,
+ "logo": "/images/UE0AAAAAFFYDMgAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1381341600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181234,
+ "id": 341181436,
+ "logo": "/images/UE0AAAAAFFYDMgAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1381428000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586389,
+ "id": 138586391,
+ "logo": "/images/UE0AAAAACEKpFQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1381512600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586393,
+ "id": 138586395,
+ "logo": "/images/UE0AAAAACEKpGQAAAAhDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937241
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937242
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937244
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937245
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937246
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937241
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937242
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937244
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937245
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937246
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1381586400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586397,
+ "id": 138586399,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1381672800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586401,
+ "id": 138586403,
+ "logo": "/images/UE0AAAAACEKpIQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1381773600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586405,
+ "id": 138586407,
+ "logo": "/images/UE0AAAAACEKpJQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1381860000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181233,
+ "id": 341181435,
+ "logo": "/images/UE0AAAAAFFYDMQAAAAhDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1381946400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181237,
+ "id": 341181442,
+ "logo": "/images/UE0AAAAAFFYDNQAAAAhDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1382032800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586409,
+ "id": 138586411,
+ "logo": "/images/UE0AAAAACEKpKQAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1382119200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586413,
+ "id": 138586415,
+ "logo": "/images/UE0AAAAACEKpLQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1382277600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586417,
+ "id": 138586419,
+ "logo": "/images/UE0AAAAACEKpMQAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1382378400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586421,
+ "id": 138586423,
+ "logo": "/images/UE0AAAAACEKpNQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1382464800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181238,
+ "id": 341181444,
+ "logo": "/images/UE0AAAAAFFYDNgAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1382551200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181238,
+ "id": 341181443,
+ "logo": "/images/UE0AAAAAFFYDNgAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1382637600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586425,
+ "id": 138586427,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937235
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937236
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937238
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937239
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937235
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937236
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937238
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937239
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1382724000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586429,
+ "id": 138586431,
+ "logo": "/images/UE0AAAAACEKpPQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1382810400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586433,
+ "id": 138586435,
+ "logo": "/images/UE0AAAAACEKpQQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1382886000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586437,
+ "id": 138586439,
+ "logo": "/images/UE0AAAAACEKpRQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383073200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586441,
+ "id": 138586443,
+ "logo": "/images/UE0AAAAACEKpSQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383246000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586445,
+ "id": 138586447,
+ "logo": "/images/UE0AAAAACEKpTQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383332400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586449,
+ "id": 138586451,
+ "logo": "/images/UE0AAAAACEKpUQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383418800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742592,
+ "id": 342742708,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383555600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742592,
+ "id": 342742709,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383562800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586453,
+ "id": 138586455,
+ "logo": "/images/UE0AAAAACEKpVQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937295
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937296
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937295
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937296
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383591600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742592,
+ "id": 342742710,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383642000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742592,
+ "id": 342742711,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383649200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742592,
+ "id": 342742712,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383728400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742592,
+ "id": 342742713,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383735600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742592,
+ "id": 342742714,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383814800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742592,
+ "id": 342742715,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383822000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586457,
+ "id": 138586459,
+ "logo": "/images/UE0AAAAACEKpWQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383850800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586461,
+ "id": 138586463,
+ "logo": "/images/UE0AAAAACEKpXQAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1383937200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586465,
+ "id": 138586467,
+ "logo": "/images/UE0AAAAACEKpYQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1384110000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586469,
+ "id": 138586471,
+ "logo": "/images/UE0AAAAACEKpZQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 90250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937289
+ },
+ {
+ "amount": 71250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937290
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937292
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937293
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937289
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937290
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937292
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937293
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1384196400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586473,
+ "id": 138586475,
+ "logo": "/images/UE0AAAAACEKpaQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1384282800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586477,
+ "id": 138586479,
+ "logo": "/images/UE0AAAAACEKpbQAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1384369200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586481,
+ "id": 138586483,
+ "logo": "/images/UE0AAAAACEKpcQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1384455600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586485,
+ "id": 138586487,
+ "logo": "/images/UE0AAAAACEKpdQAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1384542000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586501,
+ "id": 138586503,
+ "logo": "/images/UE0AAAAACEKphQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1384801200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586505,
+ "id": 138586507,
+ "logo": "/images/UE0AAAAACEKpiQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1384887600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586509,
+ "id": 138586511,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937235
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937236
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937238
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937239
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937235
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937236
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937238
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937239
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1385146800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586513,
+ "id": 138586515,
+ "logo": "/images/UE0AAAAACEKpkQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1385231400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586517,
+ "id": 138586519,
+ "logo": "/images/UE0AAAAACEKplQAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1385305200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586521,
+ "id": 138586523,
+ "logo": "/images/UE0AAAAACEKpmQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 152000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 104500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1385492400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181235,
+ "id": 341181439,
+ "logo": "/images/UE0AAAAAFFYDMwAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1385665200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586525,
+ "id": 138586527,
+ "logo": "/images/UE0AAAAACEKpnQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1385751600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586529,
+ "id": 138586531,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937241
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937242
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937244
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937245
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937246
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937241
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937242
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937244
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937245
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937246
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1385823600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181235,
+ "id": 341181438,
+ "logo": "/images/UE0AAAAAFFYDMwAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1385838000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586533,
+ "id": 138586535,
+ "logo": "/images/UE0AAAAACEKppQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1385910000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586537,
+ "id": 138586539,
+ "logo": "/images/UE0AAAAACEKpqQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386010800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586541,
+ "id": 138586543,
+ "logo": "/images/UE0AAAAACEKprQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386097200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181236,
+ "id": 341181440,
+ "logo": "/images/UE0AAAAAFFYDNAAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386183600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181236,
+ "id": 341181441,
+ "logo": "/images/UE0AAAAAFFYDNAAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386270000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586545,
+ "id": 138586547,
+ "logo": "/images/UE0AAAAACEKpsQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 104500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386356400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586549,
+ "id": 138586551,
+ "logo": "/images/UE0AAAAACEKptQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386428400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586553,
+ "id": 138586555,
+ "logo": "/images/UE0AAAAACEKpuQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386442800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586557,
+ "id": 138586559,
+ "logo": "/images/UE0AAAAACEKpvQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386514800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742593,
+ "id": 342742716,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386579600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742593,
+ "id": 342742717,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386586800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586561,
+ "id": 138586563,
+ "logo": "/images/UE0AAAAACEKpwQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386615600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742593,
+ "id": 342742718,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386666000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742593,
+ "id": 342742719,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386673200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586565,
+ "id": 138586567,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386702000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742593,
+ "id": 342742720,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386752400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742593,
+ "id": 342742721,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386759600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181241,
+ "id": 341181449,
+ "logo": "/images/UE0AAAAAFFYDOQAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386788400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742593,
+ "id": 342742722,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386838800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742593,
+ "id": 342742723,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386846000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181241,
+ "id": 341181450,
+ "logo": "/images/UE0AAAAAFFYDOQAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386874800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586569,
+ "id": 138586571,
+ "logo": "/images/UE0AAAAACEKpyQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1386961200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742594,
+ "id": 342742724,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387184400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742594,
+ "id": 342742725,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387191600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586581,
+ "id": 138586583,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264860
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264861
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264863
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264864
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264860
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264861
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264863
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264864
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387220400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742594,
+ "id": 342742726,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387270800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742594,
+ "id": 342742727,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387278000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586585,
+ "id": 138586587,
+ "logo": "/images/UE0AAAAACEKp2QAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387306800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742594,
+ "id": 342742728,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387357200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742594,
+ "id": 342742729,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387364400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181250,
+ "id": 341181465,
+ "logo": "/images/UE0AAAAAFFYDQgAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387393200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742594,
+ "id": 342742730,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387443600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742594,
+ "id": 342742731,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387450800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586589,
+ "id": 138586591,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387566000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586593,
+ "id": 138586595,
+ "logo": "/images/UE0AAAAACEKp4QAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937235
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937236
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937238
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937239
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937235
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937236
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937238
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937239
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387724400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742595,
+ "id": 342742732,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387789200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742595,
+ "id": 342742733,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387796400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742595,
+ "id": 342742734,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387875600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742595,
+ "id": 342742735,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387882800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742595,
+ "id": 342742736,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387962000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742595,
+ "id": 342742737,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1387969200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742595,
+ "id": 342742738,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1388048400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742595,
+ "id": 342742739,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1388055600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742596,
+ "id": 342742740,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1388998800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742596,
+ "id": 342742741,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389006000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742596,
+ "id": 342742742,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389085200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742596,
+ "id": 342742743,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389092400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742596,
+ "id": 342742744,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389171600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742596,
+ "id": 342742745,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389178800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181245,
+ "id": 341181458,
+ "logo": "/images/UE0AAAAAFFYDPQAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389207600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742596,
+ "id": 342742746,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389258000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 342742596,
+ "id": 342742747,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 180500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 342752287,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 342752792
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389265200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181245,
+ "id": 341181457,
+ "logo": "/images/UE0AAAAAFFYDPQAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389294000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586597,
+ "id": 138586599,
+ "logo": "/images/UE0AAAAACEKp5QAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389380400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586601,
+ "id": 138586603,
+ "logo": "/images/UE0AAAAACEKp6QAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937241
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937242
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937244
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937245
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937246
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937241
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937242
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937244
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937245
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937246
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389452400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586605,
+ "id": 138586607,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389538800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586609,
+ "id": 138586611,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 15000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937314
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937314
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389726000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181242,
+ "id": 341181451,
+ "logo": "/images/UE0AAAAAFFYDOgAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389812400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181242,
+ "id": 341181452,
+ "logo": "/images/UE0AAAAAFFYDOgAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389898800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586613,
+ "id": 138586615,
+ "logo": "/images/UE0AAAAACEKp9QAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086215
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086215
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1389985200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586617,
+ "id": 138586619,
+ "logo": "/images/UE0AAAAACEKp+QAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 90250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 71250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1390071600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586625,
+ "id": 138586627,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937235
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937236
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937238
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937239
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937235
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937236
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937238
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937239
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1390143600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586629,
+ "id": 138586631,
+ "logo": "/images/UE0AAAAACEKqBQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 90250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937271
+ },
+ {
+ "amount": 71250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937272
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937274
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937275
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937271
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937272
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937274
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937275
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1390159800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181239,
+ "id": 341181446,
+ "logo": "/images/UE0AAAAAFFYDNwAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1390417200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181239,
+ "id": 341181445,
+ "logo": "/images/UE0AAAAAFFYDNwAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1390503600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586633,
+ "id": 138586635,
+ "logo": "/images/UE0AAAAACEKqCQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1390590000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586641,
+ "id": 138586643,
+ "logo": "/images/UE0AAAAACEKqEQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1390676400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586645,
+ "id": 138586647,
+ "logo": "/images/UE0AAAAACEKqFQAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937235
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937236
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937238
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937239
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937235
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937236
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937238
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937239
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1390748400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586649,
+ "id": 138586651,
+ "logo": "/images/UE0AAAAACEKqGQAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1390849200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586653,
+ "id": 138586655,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1390935600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181243,
+ "id": 341181453,
+ "logo": "/images/UE0AAAAAFFYDOwAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1391022000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181243,
+ "id": 341181454,
+ "logo": "/images/UE0AAAAAFFYDOwAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1391108400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586657,
+ "id": 138586659,
+ "logo": "/images/UE0AAAAACEKqIQAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1391194800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586661,
+ "id": 138586663,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1391353200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586661,
+ "id": 138586665,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1391367600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586667,
+ "id": 138586669,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937295
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937296
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937295
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937296
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1391540400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586671,
+ "id": 138586673,
+ "logo": "/images/UE0AAAAACEKqLwAAAAlDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937289
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937290
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937292
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937293
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937289
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937290
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937292
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937293
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1391626800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586675,
+ "id": 138586677,
+ "logo": "/images/UE0AAAAACEKqMwAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1391713200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586679,
+ "id": 138586681,
+ "logo": "/images/UE0AAAAACEKqNwAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1391799600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586687,
+ "id": 138586689,
+ "logo": "/images/UE0AAAAACEKqPwAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 90250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 71250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1391886000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586691,
+ "id": 138586693,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1391958000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586695,
+ "id": 138586697,
+ "logo": "/images/UE0AAAAACEKqRwAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1392145200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181240,
+ "id": 341181448,
+ "logo": "/images/UE0AAAAAFFYDOAAAAAhDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1392231600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181240,
+ "id": 341181447,
+ "logo": "/images/UE0AAAAAFFYDOAAAAAhDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1392318000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586699,
+ "id": 138586701,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 15000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264872
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264872
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1392404400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586703,
+ "id": 138586705,
+ "logo": "/images/UE0AAAAACEKqTwAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1392490800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586707,
+ "id": 138586709,
+ "logo": "/images/UE0AAAAACEKqUwAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1392562800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586711,
+ "id": 138586713,
+ "logo": "/images/UE0AAAAACEKqVwAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1392663600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586715,
+ "id": 138586717,
+ "logo": "/images/UE0AAAAACEKqWwAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1392750000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181248,
+ "id": 341181462,
+ "logo": "/images/UE0AAAAAFFYDQAAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1392836400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586719,
+ "id": 138586721,
+ "logo": "/images/UE0AAAAACEKqXwAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1393095600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586723,
+ "id": 138586729,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937307
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937308
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937310
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937311
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937312
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937307
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937308
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937310
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937311
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937312
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1393678800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586723,
+ "id": 138586725,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937307
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937308
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937310
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937311
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937312
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937307
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937308
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937310
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937311
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937312
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1393693200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586723,
+ "id": 138586727,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937307
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937308
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937310
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937311
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937312
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937307
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937308
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937310
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937311
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937312
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1393754400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586731,
+ "id": 138586733,
+ "logo": "/images/UE0AAAAACEKqawAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1393959600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181249,
+ "id": 341181463,
+ "logo": "/images/UE0AAAAAFFYDQQAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1394046000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181249,
+ "id": 341181464,
+ "logo": "/images/UE0AAAAAFFYDQQAAAAdDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1394132400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586735,
+ "id": 138586737,
+ "logo": "/images/UE0AAAAACEKqbwAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1394218800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586743,
+ "id": 138586745,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1394305200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586747,
+ "id": 138586749,
+ "logo": "/images/UE0AAAAACEKqewAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1394377200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586751,
+ "id": 138586753,
+ "logo": "/images/UE0AAAAACEKqfwAAAAVDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1394478000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181244,
+ "id": 341181455,
+ "logo": "/images/UE0AAAAAFFYDPAAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1394650800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181244,
+ "id": 341181456,
+ "logo": "/images/UE0AAAAAFFYDPAAAAAZDSVRN",
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1394737200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586755,
+ "id": 138586757,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264866
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264867
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264869
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264870
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264866
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264867
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264869
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264870
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1394823600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586759,
+ "id": 138586761,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1395082800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 151183114,
+ "id": 151183116,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 90250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937289
+ },
+ {
+ "amount": 71250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937290
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937292
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937293
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937289
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937290
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937292
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937293
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1395169200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586771,
+ "id": 138586773,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1395255600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586775,
+ "id": 138586777,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1395342000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586779,
+ "id": 138586781,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1395428400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586783,
+ "id": 138586785,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937241
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937242
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937244
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937245
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937246
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937241
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937242
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937244
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937245
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937246
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1395500400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586787,
+ "id": 138586789,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1395514800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586795,
+ "id": 138586797,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937235
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937236
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937238
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937239
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937235
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937236
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937238
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937239
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1395586800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181246,
+ "id": 341181459,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1395860400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181246,
+ "id": 341181460,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826019
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1395946800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586799,
+ "id": 138586801,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1396033200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586803,
+ "id": 138586805,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 90250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 71250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1396191600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586807,
+ "id": 138586809,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 104500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1396288800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586811,
+ "id": 138586813,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 90250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 71250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1396375200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181255,
+ "id": 341181472,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1396461600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181255,
+ "id": 341181471,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1396548000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586815,
+ "id": 138586817,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1396634400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586823,
+ "id": 138586825,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1396720800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586827,
+ "id": 138586829,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1396792800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586831,
+ "id": 138586833,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1396893600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586835,
+ "id": 138586837,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1396980000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181256,
+ "id": 341181473,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1397066400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181256,
+ "id": 341181474,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1397152800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586839,
+ "id": 138586841,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264866
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264867
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264869
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264870
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264866
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264867
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264869
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264870
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1397239200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586843,
+ "id": 138586845,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1397311200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586847,
+ "id": 138586849,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1397325600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586851,
+ "id": 138586853,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1397397600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586855,
+ "id": 138586857,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1397498400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586859,
+ "id": 138586861,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1397584800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586863,
+ "id": 138586865,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1397930400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181251,
+ "id": 341181466,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1398276000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181253,
+ "id": 341181468,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1398362400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586867,
+ "id": 138586869,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1398448800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586871,
+ "id": 138586873,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1398607200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586875,
+ "id": 138586877,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1398708000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586879,
+ "id": 138586881,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 171000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 123500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 66500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1398794400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586883,
+ "id": 138586887,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1399125600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586883,
+ "id": 138586885,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1399140000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586889,
+ "id": 138586891,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937307
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937308
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937310
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937311
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937307
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937308
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937310
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937311
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1399312800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586893,
+ "id": 138586895,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1399399200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181232,
+ "id": 341181434,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1399485600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586897,
+ "id": 138586899,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1399917600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586901,
+ "id": 138586903,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1400176800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586905,
+ "id": 138586907,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1400263200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586909,
+ "id": 138586911,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1400349600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586917,
+ "id": 138586919,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937235
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937236
+ },
+ {
+ "amount": 19000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937238
+ },
+ {
+ "amount": 14250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937239
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937235
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937236
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937238
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937239
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937240
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1400421600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181259,
+ "id": 341181480,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1400695200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181259,
+ "id": 341181479,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 80750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826016
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826017
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826015
+ },
+ {
+ "amount": 28500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 340826018
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826016
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826017
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826015
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 340826018
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1400781600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586921,
+ "id": 138586923,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086210
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086211
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086213
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086210
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086211
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086214
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1400868000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586929,
+ "id": 138586931,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937241
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937242
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937244
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937245
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937246
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937241
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937242
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937244
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937245
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937246
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1400940000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586933,
+ "id": 138586935,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1401026400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586937,
+ "id": 138586939,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1401127200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586941,
+ "id": 138586943,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264860
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264861
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264863
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341264864
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264860
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264861
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264863
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341264864
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1401472800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586945,
+ "id": 138586947,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1401730200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586949,
+ "id": 138586951,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1401818400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586953,
+ "id": 138586955,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1401904800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586957,
+ "id": 138586959,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1401991200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586961,
+ "id": 138586963,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1402077600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586965,
+ "id": 138586967,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 95000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1402423200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181258,
+ "id": 341181477,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1402509600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181258,
+ "id": 341181478,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1402596000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586969,
+ "id": 138586971,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086196
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086196
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 339086197
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1402768800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586973,
+ "id": 138586975,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1402840800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586977,
+ "id": 138586979,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 33250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 23750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 16150,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1402941600000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586981,
+ "id": 138586983,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 123500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937289
+ },
+ {
+ "amount": 85500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937290
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937292
+ },
+ {
+ "amount": 38000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937293
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937294
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937289
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937290
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937292
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937293
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937294
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1403028000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181257,
+ "id": 341181475,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1403114400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181257,
+ "id": 341181476,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1403200800000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 341181247,
+ "id": 341181461,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 57000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179212
+ },
+ {
+ "amount": 42750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179213
+ },
+ {
+ "amount": 32300,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179214
+ },
+ {
+ "amount": 20900,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179215
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179212
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179213
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179214
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179215
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 341179216
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1403719200000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586989,
+ "id": 138586991,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 152000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937284
+ },
+ {
+ "amount": 104500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937285
+ },
+ {
+ "amount": 76000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937287
+ },
+ {
+ "amount": 52250,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937288
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937284
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937285
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937287
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937288
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937283
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1403892000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586993,
+ "id": 138586995,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 123500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 85500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 38000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1404324000000,
+ "venueCode": "PLEYEL_PLEYEL"
+ },
+ {
+ "eventId": 138586997,
+ "id": 138586999,
+ "logo": null,
+ "name": null,
+ "prices": [
+ {
+ "amount": 123500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937277
+ },
+ {
+ "amount": 85500,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937278
+ },
+ {
+ "amount": 61750,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937280
+ },
+ {
+ "amount": 38000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937281
+ },
+ {
+ "amount": 10000,
+ "audienceSubCategoryId": 337100890,
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatCategories": [
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937277
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705999,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937278
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705998,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705995,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705996,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205705993,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706007,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937280
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205705994,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706006,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706001,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706000,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937281
+ },
+ {
+ "areas": [
+ {
+ "areaId": 205706005,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706004,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706003,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706002,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706009,
+ "blockIds": []
+ },
+ {
+ "areaId": 205706008,
+ "blockIds": []
+ }
+ ],
+ "seatCategoryId": 338937282
+ }
+ ],
+ "seatMapImage": null,
+ "start": 1404410400000,
+ "venueCode": "PLEYEL_PLEYEL"
+ }
+ ],
+ "seatCategoryNames": {
+ "338937235": "1ère catégorie",
+ "338937236": "2ème catégorie",
+ "338937238": "3ème catégorie",
+ "338937239": "4ème catégorie",
+ "338937240": "5ème catégorie",
+ "338937241": "1ère catégorie",
+ "338937242": "2ème catégorie",
+ "338937244": "3ème catégorie",
+ "338937245": "4ème catégorie",
+ "338937246": "5ème catégorie",
+ "338937271": "1ère catégorie",
+ "338937272": "2ème catégorie",
+ "338937274": "3ème catégorie",
+ "338937275": "4ème catégorie",
+ "338937277": "1ère catégorie",
+ "338937278": "2ème catégorie",
+ "338937280": "3ème catégorie",
+ "338937281": "4ème catégorie",
+ "338937282": "5ème catégorie",
+ "338937283": "5ème catégorie",
+ "338937284": "1ère catégorie",
+ "338937285": "2ème catégorie",
+ "338937287": "3ème catégorie",
+ "338937288": "4ème catégorie",
+ "338937289": "1ère catégorie",
+ "338937290": "2ème catégorie",
+ "338937292": "3ème catégorie",
+ "338937293": "4ème catégorie",
+ "338937294": "5ème catégorie",
+ "338937295": "1ère catégorie",
+ "338937296": "2ème catégorie",
+ "338937307": "1ère catégorie",
+ "338937308": "2ème catégorie",
+ "338937310": "3ème catégorie",
+ "338937311": "4ème catégorie",
+ "338937312": "5ème catégorie",
+ "338937314": "Catégorie unique",
+ "339086196": "1ère catégorie",
+ "339086197": "2ème catégorie",
+ "339086210": "1ère catégorie",
+ "339086211": "2ème catégorie",
+ "339086213": "3ème catégorie",
+ "339086214": "4ème catégorie",
+ "339086215": "5ème catégorie",
+ "340826015": "Catégorie 3",
+ "340826016": "Catégorie 1",
+ "340826017": "Catégorie 2",
+ "340826018": "Catégorie 4",
+ "340826019": "Catégorie 5",
+ "341179212": "CAT1",
+ "341179213": "CAT2",
+ "341179214": "CAT3",
+ "341179215": "CAT4",
+ "341179216": "CAT5",
+ "341264860": "1ère catégorie",
+ "341264861": "2ème catégorie",
+ "341264863": "3ème catégorie",
+ "341264864": "4ème catégorie",
+ "341264866": "1ère catégorie",
+ "341264867": "2ème catégorie",
+ "341264869": "3ème catégorie",
+ "341264870": "4ème catégorie",
+ "341264872": "1ère catégorie",
+ "342752792": "catétgorie unique"
+ },
+ "subTopicNames": {
+ "337184262": "Musique amplifiée",
+ "337184263": "Musique baroque",
+ "337184267": "Ciné-concert",
+ "337184268": "Musique classique",
+ "337184269": "Jazz",
+ "337184273": "Musique de chambre",
+ "337184275": "Musique dirigée",
+ "337184279": "Musique du monde",
+ "337184280": "Pop/rock",
+ "337184281": "Musique de chambre",
+ "337184282": "Famille",
+ "337184283": "Concert",
+ "337184284": "Opéra (version de concert)",
+ "337184288": "Musique contemporaine",
+ "337184292": "Musique vocale",
+ "337184296": "Musique ancienne",
+ "337184297": "Chanson",
+ "337184298": "Voix",
+ "337184299": "famille"
+ },
+ "subjectNames": {},
+ "topicNames": {
+ "107888604": "Activité",
+ "324846098": "Type de public",
+ "324846099": "Genre",
+ "324846100": "Formations musicales"
+ },
+ "topicSubTopics": {
+ "107888604": [
+ 337184283,
+ 337184267
+ ],
+ "324846098": [
+ 337184299
+ ],
+ "324846099": [
+ 337184268,
+ 337184288,
+ 337184284,
+ 337184263,
+ 337184298,
+ 337184269,
+ 337184280,
+ 337184297,
+ 337184281,
+ 337184296,
+ 337184279
+ ],
+ "324846100": [
+ 337184275,
+ 337184262,
+ 337184292,
+ 337184273,
+ 337184282
+ ]
+ },
+ "venueNames": {
+ "PLEYEL_PLEYEL": "Salle Pleyel"
+ }
+} \ No newline at end of file
diff --git a/benchmark/src/jmh/resources/twitter.json b/benchmark/src/jmh/resources/twitter.json
new file mode 100644
index 00000000..f57c6548
--- /dev/null
+++ b/benchmark/src/jmh/resources/twitter.json
@@ -0,0 +1 @@
+{"statuses":[{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:26 +0000 2011","user":{"profile_link_color":"140e13","protected":false,"default_profile_image":false,"following":true,"created_at":"Sat Apr 03 17:18:50 +0000 2010","friends_count":793,"name":"Keyla Flores G.","notifications":false,"profile_background_color":"1f181d","is_translator":false,"statuses_count":7818,"profile_background_tile":true,"utc_offset":-36000,"description":"25. Margarite\u00f1isima, del signo Leo, salgo todos los dias a trabajar para tener lo que quiero, mi tiempo es oro no me hagas perderlo. ","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/360355023\/IMG-20111106-00051.jpg","favourites_count":18,"profile_sidebar_fill_color":"75e3ff","follow_request_sent":true,"geo_enabled":false,"profile_sidebar_border_color":"405ed6","location":"Habitaci\u00f3n con vista al mar","show_all_inline_media":false,"lang":"es","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1665180289\/330642345_normal.jpg","screen_name":"KeysSFlores","listed_count":5,"verified":false,"profile_use_background_image":true,"time_zone":"Hawaii","profile_text_color":"f5f5f5","id":129248213,"id_str":"129248213","contributors_enabled":false,"profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/360355023\/IMG-20111106-00051.jpg","followers_count":383,"profile_image_url":"http:\/\/a1.twimg.com\/profile_images\/1665180289\/330642345_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[{"name":"Nelson Herrero","indices":[29,39],"screen_name":"nhgdesign","id":28721814,"id_str":"28721814"}]},"source":"\u003Ca href=\"http:\/\/ubersocial.com\" rel=\"nofollow\"\u003EUberSocial for BlackBerry\u003C\/a\u003E","id":144179670739456000,"id_str":"144179670739456000","text":"No te perdiste de nada bueno @nhgdesign"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:26 +0000 2011","user":{"profile_link_color":"0084B4","protected":false,"default_profile_image":false,"following":true,"created_at":"Sun Nov 28 05:04:29 +0000 2010","friends_count":279,"name":"Adrian Mendoza ","notifications":false,"profile_background_color":"C0DEED","is_translator":false,"statuses_count":15484,"profile_background_tile":true,"utc_offset":-18000,"description":"Cuenta NO VERIFICADA \u2611 Ah tod@ que me sigue le Devuelvo el follow! Te amoo CLUBSPORTEMELEC ! ","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/264010981\/2441267062352500195.jpg","favourites_count":39,"profile_sidebar_fill_color":"DDEEF6","follow_request_sent":true,"geo_enabled":true,"profile_sidebar_border_color":"C0DEED","location":"Lejos de aqui, cerca de alla","show_all_inline_media":false,"lang":"es","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1677214145\/331007715_normal.jpg","screen_name":"Adrian_Cse","listed_count":6,"verified":false,"profile_use_background_image":true,"url":"http:\/\/www.qechuchateimporta.com","time_zone":"Quito","profile_text_color":"333333","id":220569715,"id_str":"220569715","contributors_enabled":false,"profile_background_image_url":"http:\/\/a3.twimg.com\/profile_background_images\/264010981\/2441267062352500195.jpg","followers_count":415,"profile_image_url":"http:\/\/a2.twimg.com\/profile_images\/1677214145\/331007715_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[]},"source":"\u003Ca href=\"http:\/\/ubersocial.com\" rel=\"nofollow\"\u003EUberSocial for BlackBerry\u003C\/a\u003E","id":144179670630400000,"id_str":"144179670630400000","text":"Y ya lo ve, y ya lo ve! El que no Retuitea No es de Emelec!!"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:26 +0000 2011","user":{"profile_link_color":"ff00ae","protected":false,"default_profile_image":false,"following":true,"created_at":"Mon Nov 14 14:17:25 +0000 2011","friends_count":94,"name":"Tht Bitch___Mirahh ,","notifications":false,"profile_background_color":"642D8B","is_translator":false,"statuses_count":253,"profile_background_tile":true,"utc_offset":null,"description":"\u200e, Follow a REAL bitchhh @Beauty_Mirah \u2665 imma follow backk doeee ?` Simply Beautiful (Mirahh )\r\n\r\n\u263b \u2665\u263b\r\n\/\u2588\\.\/\u2588\\ \r\n.||. .||. T a k e n '","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/371911266\/311520_206821182708056_100001405288487_527713_5677025_n__18_.jpg","favourites_count":0,"profile_sidebar_fill_color":"a97ced","follow_request_sent":true,"geo_enabled":false,"profile_sidebar_border_color":"66d5d9","location":"","show_all_inline_media":false,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1662775002\/379841_247431711982506_100001470361390_737322_191639635_n_normal.jpg","screen_name":"Beauty_Mirah","listed_count":0,"verified":false,"profile_use_background_image":true,"time_zone":null,"profile_text_color":"365cf5","id":412296859,"id_str":"412296859","contributors_enabled":false,"profile_background_image_url":"http:\/\/a3.twimg.com\/profile_background_images\/371911266\/311520_206821182708056_100001405288487_527713_5677025_n__18_.jpg","followers_count":71,"profile_image_url":"http:\/\/a2.twimg.com\/profile_images\/1662775002\/379841_247431711982506_100001470361390_737322_191639635_n_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[]},"source":"web","id":144179670018048000,"id_str":"144179670018048000","text":"iI Hate When Gurl Call Yu Dey Sis Orr RiteHand Ndd They Wanna Tlk Bout Yu Behide Yall Bck Sayin O iI Dnt Hang Wiff Her , Smh"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:26 +0000 2011","user":{"profile_link_color":"0084B4","protected":false,"default_profile_image":false,"following":true,"created_at":"Tue Nov 29 02:18:55 +0000 2011","friends_count":11,"name":"\u307e\u3086\u3086\u3093","notifications":false,"profile_background_color":"C0DEED","is_translator":false,"statuses_count":131,"profile_background_tile":false,"utc_offset":32400,"description":"JK2\u306e\uff81\uff71\u90e8\u6240\u5c5e\u3002","default_profile":true,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme1\/bg.png","favourites_count":0,"profile_sidebar_fill_color":"DDEEF6","follow_request_sent":true,"geo_enabled":false,"profile_sidebar_border_color":"C0DEED","location":"","show_all_inline_media":false,"lang":"ja","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1664209309\/image_normal.jpg","screen_name":"piyomau","listed_count":0,"verified":false,"profile_use_background_image":true,"time_zone":"Tokyo","profile_text_color":"333333","id":423894006,"id_str":"423894006","contributors_enabled":false,"profile_background_image_url":"http:\/\/a0.twimg.com\/images\/themes\/theme1\/bg.png","followers_count":9,"profile_image_url":"http:\/\/a2.twimg.com\/profile_images\/1664209309\/image_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[]},"source":"\u003Ca href=\"http:\/\/twtr.jp\" rel=\"nofollow\"\u003EKeitai Web\u003C\/a\u003E","id":144179669690880000,"id_str":"144179669690880000","text":"\u304a\u306f\u3088\u3001\u304a\u3084\u3059\u307f\u3002"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:26 +0000 2011","user":{"profile_link_color":"009999","protected":false,"default_profile_image":false,"following":true,"created_at":"Wed Jan 12 20:46:14 +0000 2011","friends_count":189,"name":"Courtney Simmons","notifications":false,"profile_background_color":"131516","is_translator":false,"statuses_count":1111,"profile_background_tile":true,"utc_offset":null,"description":"","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme14\/bg.gif","favourites_count":40,"profile_sidebar_fill_color":"efefef","follow_request_sent":true,"geo_enabled":false,"profile_sidebar_border_color":"eeeeee","location":"","show_all_inline_media":false,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1618741468\/profile_image_1320238417755_normal.jpg","screen_name":"_See_Sims","listed_count":0,"verified":false,"profile_use_background_image":true,"time_zone":null,"profile_text_color":"333333","id":237430721,"id_str":"237430721","contributors_enabled":false,"profile_background_image_url":"http:\/\/a1.twimg.com\/images\/themes\/theme14\/bg.gif","followers_count":79,"profile_image_url":"http:\/\/a1.twimg.com\/profile_images\/1618741468\/profile_image_1320238417755_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[]},"source":"\u003Ca href=\"http:\/\/ubersocial.com\" rel=\"nofollow\"\u003EUberSocial for Android\u003C\/a\u003E","id":144179669275648000,"id_str":"144179669275648000","text":"I just got off work and I see my ppl been talking shit all day."},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:26 +0000 2011","possibly_sensitive":false,"user":{"profile_link_color":"000000","protected":false,"default_profile_image":false,"following":true,"created_at":"Thu Oct 28 16:01:49 +0000 2010","friends_count":299,"name":"\u261c\u2550\u32e1 Eu te amo!","notifications":false,"profile_background_color":"ffffff","is_translator":false,"statuses_count":6187,"profile_background_tile":true,"utc_offset":-10800,"description":"A vida \u00e9 minha, mas o cora\u00e7\u00e3o.. \u00e9 seu. O sorriso \u00e9 meu, mas o motivo.. \u00e9 voc\u00ea. @thomasrestart. Recome\u00e7ando pela terceira vez,quem ama,nunca desiste. 04-11-11 \u2665","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/376916405\/stripe_f3abb839ab6e6e90b35c6315d976663c.png","favourites_count":69,"profile_sidebar_fill_color":"efefef","follow_request_sent":true,"geo_enabled":false,"profile_sidebar_border_color":"eeeeee","location":"Santos\/SP","show_all_inline_media":false,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1677195709\/meu_avatyar_normal.png","screen_name":"nextoyouthomas","listed_count":43,"verified":false,"profile_use_background_image":true,"url":"http:\/\/letters-for-thomas.tumblr.com","time_zone":"Greenland","profile_text_color":"e0e0e0","id":209142229,"id_str":"209142229","contributors_enabled":false,"profile_background_image_url":"http:\/\/a1.twimg.com\/profile_background_images\/376916405\/stripe_f3abb839ab6e6e90b35c6315d976663c.png","followers_count":708,"profile_image_url":"http:\/\/a1.twimg.com\/profile_images\/1677195709\/meu_avatyar_normal.png"},"retweeted":false,"truncated":false,"entities":{"urls":[{"expanded_url":"http:\/\/bit.ly\/s1M7s6","indices":[44,64],"url":"http:\/\/t.co\/QlSc81ex","display_url":"bit.ly\/s1M7s6"}],"hashtags":[],"user_mentions":[]},"source":"web","id":144179669057536000,"id_str":"144179669057536000","text":"Entrevista exclusiva com Pe Lu da Restart - http:\/\/t.co\/QlSc81ex"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:25 +0000 2011","user":{"profile_link_color":"2FC2EF","protected":false,"default_profile_image":false,"following":true,"created_at":"Thu Oct 06 07:58:39 +0000 2011","friends_count":69,"name":"\u304a\u3080\u304a\u3080","notifications":false,"profile_background_color":"1A1B1F","is_translator":false,"statuses_count":1492,"profile_background_tile":true,"utc_offset":32400,"description":"TDG\u3067\u30b0\u30e9\u30d5\u30a3\u30c3\u30af\u30c7\u30b6\u30a4\u30f3\u5b66\u3073\u4e2d\u3067\u3059\u3063( \uff65\u03c9\uff65)\u30aa\u30e0\u30e9\u30a4\u30b9\u3068\u30de\u30e8\u30cd\u30fc\u30ba\u304c\u3059\u3054\u304f\u5927\u597d\u304d\u3067\u3059(*\u00b4_\uff40*)\u3088\u308d\u3057\u304f\u3067\u3059","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme9\/bg.gif","favourites_count":0,"profile_sidebar_fill_color":"252429","follow_request_sent":true,"geo_enabled":false,"profile_sidebar_border_color":"181A1E","location":"\u30aa\u30e0\u30e9\u30a4\u30b9\u306e\u4e2d","show_all_inline_media":false,"lang":"ja","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1575150809\/DCF00179_normal.jpg","screen_name":"omuomu52","listed_count":0,"verified":false,"profile_use_background_image":true,"time_zone":"Tokyo","profile_text_color":"666666","id":385858091,"id_str":"385858091","contributors_enabled":false,"profile_background_image_url":"http:\/\/a1.twimg.com\/images\/themes\/theme9\/bg.gif","followers_count":70,"profile_image_url":"http:\/\/a1.twimg.com\/profile_images\/1575150809\/DCF00179_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[]},"source":"\u003Ca href=\"http:\/\/twtr.jp\" rel=\"nofollow\"\u003EKeitai Web\u003C\/a\u003E","id":144179666654208000,"id_str":"144179666654208000","text":"\u8ab2\u984c\u7d42\u308f\u3089\u306a\u304b\u3063\u305f\u30fc\u2026\u6388\u696d\u5185\u63d0\u51fa\u3060\u304b\u3089\u306a\u3093\u3068\u304b\u306a\u308b\u3051\u3069\u7d19\u304c\u306a\u3042"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:25 +0000 2011","user":{"profile_link_color":"948294","protected":false,"default_profile_image":false,"following":true,"created_at":"Mon May 09 05:36:22 +0000 2011","friends_count":350,"name":"Chuck Conkle","notifications":false,"profile_background_color":"3b6980","is_translator":false,"statuses_count":2156,"profile_background_tile":true,"utc_offset":-18000,"description":"the term Radmaster McFull On Sweetness gets tossed around a lot these days...","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme14\/bg.gif","favourites_count":16,"profile_sidebar_fill_color":"c5d2d6","follow_request_sent":true,"geo_enabled":true,"profile_sidebar_border_color":"ffffff","location":"columbus OH","show_all_inline_media":false,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1454850214\/sentinel_normal.jpg","screen_name":"theFakeChuck","listed_count":3,"verified":false,"profile_use_background_image":true,"url":"http:\/\/www.facebook.com\/darthchQ","time_zone":"Eastern Time (US & Canada)","profile_text_color":"333333","id":295517737,"id_str":"295517737","contributors_enabled":false,"profile_background_image_url":"http:\/\/a1.twimg.com\/images\/themes\/theme14\/bg.gif","followers_count":113,"profile_image_url":"http:\/\/a1.twimg.com\/profile_images\/1454850214\/sentinel_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[]},"source":"\u003Ca href=\"http:\/\/twitter.com\/#!\/download\/iphone\" rel=\"nofollow\"\u003ETwitter for iPhone\u003C\/a\u003E","id":144179666129920000,"id_str":"144179666129920000","text":"My cousin's co-worker was killed in 9\/11 & that is definitely NOT someone i just made up to appear closer to the event."},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:25 +0000 2011","user":{"profile_link_color":"CC3366","protected":false,"default_profile_image":false,"following":true,"created_at":"Sat Mar 05 00:23:35 +0000 2011","friends_count":48,"name":"a @lepacheco_ \u00e9 minh","notifications":false,"profile_background_color":"DBE9ED","is_translator":false,"statuses_count":7792,"profile_background_tile":true,"utc_offset":-14400,"description":"Eu amo minha best @nataliamiki muito, e amo meu japa nego @ryuum, e minha diva @annajapa_ , e a @lepacheco_ \u00e9 minha vida.s2","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/370650696\/bgbgbg.jpg","favourites_count":30,"profile_sidebar_fill_color":"E6F6F9","follow_request_sent":true,"geo_enabled":false,"profile_sidebar_border_color":"DBE9ED","location":"@renatoobotelhoo ","show_all_inline_media":false,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1671317726\/302913_221830101218881_100001756783420_527892_1636378468_a_normal.jpg","screen_name":"Giu_topeira","listed_count":0,"verified":false,"profile_use_background_image":true,"time_zone":"Santiago","profile_text_color":"333333","id":260974124,"id_str":"260974124","contributors_enabled":false,"profile_background_image_url":"http:\/\/a2.twimg.com\/profile_background_images\/370650696\/bgbgbg.jpg","followers_count":144,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1671317726\/302913_221830101218881_100001756783420_527892_1636378468_a_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[{"name":"Lucas Barros","indices":[12,26],"screen_name":"LucasBarros97","id":316512025,"id_str":"316512025"}]},"source":"web","id":144179665299456000,"id_str":"144179665299456000","text":"eu vi 20h20 @LucasBarros97"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:24 +0000 2011","user":{"profile_link_color":"0084B4","protected":false,"default_profile_image":false,"following":true,"created_at":"Fri May 13 22:04:46 +0000 2011","friends_count":150,"name":"Renan ","notifications":false,"profile_background_color":"C0DEED","is_translator":false,"statuses_count":3405,"profile_background_tile":true,"utc_offset":-14400,"description":"renan da @tuti_frutt s2","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/374983673\/458198079.jpg","favourites_count":1,"profile_sidebar_fill_color":"DDEEF6","follow_request_sent":true,"geo_enabled":false,"profile_sidebar_border_color":"C0DEED","location":"","show_all_inline_media":false,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1528811915\/IMG00029_normal.jpg","screen_name":"Reenan_Vale","listed_count":0,"verified":false,"profile_use_background_image":true,"time_zone":"Santiago","profile_text_color":"333333","id":298199808,"id_str":"298199808","contributors_enabled":false,"profile_background_image_url":"http:\/\/a2.twimg.com\/profile_background_images\/374983673\/458198079.jpg","followers_count":137,"profile_image_url":"http:\/\/a3.twimg.com\/profile_images\/1528811915\/IMG00029_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[{"name":"rude demais, cuidado","indices":[2,13],"screen_name":"tuti_frutt","id":89217485,"id_str":"89217485"}]},"source":"web","id":144179662153728000,"id_str":"144179662153728000","text":"a @tuti_frutt veio aqui me ver hooje (: hahaha"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:24 +0000 2011","user":{"profile_link_color":"1234e0","protected":false,"default_profile_image":false,"following":true,"created_at":"Mon Apr 19 22:38:27 +0000 2010","friends_count":68,"name":"SarahMSalas","notifications":false,"profile_background_color":"3b5154","is_translator":false,"statuses_count":1751,"profile_background_tile":true,"utc_offset":-28800,"description":"KILLAh UNDERCOVER ","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/357594529\/samsung_mess_073.jpg","favourites_count":540,"profile_sidebar_fill_color":"000000","follow_request_sent":true,"geo_enabled":true,"profile_sidebar_border_color":"3d0a3d","location":" \u266a behind a Mic \u266a ","show_all_inline_media":true,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1673336134\/294432_256152014415006_100000606290327_972059_1510679_n_normal.jpg","screen_name":"sarybear22","listed_count":0,"verified":false,"profile_use_background_image":true,"url":"http:\/\/www.reverbnation.com\/#!\/sarahmarissa","time_zone":"Pacific Time (US & Canada)","profile_text_color":"d41e3c","id":134944657,"id_str":"134944657","contributors_enabled":false,"profile_background_image_url":"http:\/\/a1.twimg.com\/profile_background_images\/357594529\/samsung_mess_073.jpg","followers_count":81,"profile_image_url":"http:\/\/a3.twimg.com\/profile_images\/1673336134\/294432_256152014415006_100000606290327_972059_1510679_n_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[{"indices":[0,26],"text":"YouKnowAFatPersonInvented"}],"user_mentions":[]},"source":"web","id":144179660996096000,"id_str":"144179660996096000","text":"#YouKnowAFatPersonInvented the Car Stereo Remote....Like wtf realy?"},{"id_str":"144179660362752000","created_at":"Tue Dec 06 22:21:24 +0000 2011","retweet_count":0,"favorited":false,"user":{"time_zone":null,"profile_text_color":"3D1957","protected":false,"id_str":"282017664","contributors_enabled":false,"following":false,"profile_background_image_url":"http:\/\/a1.twimg.com\/profile_background_images\/361652875\/33045507467241-CharcoalDamask.jpg","created_at":"Thu Apr 14 12:02:38 +0000 2011","followers_count":21,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1632165317\/2008759090xrtujeujh_normal.jpg","name":"conchita gallion","default_profile":false,"notifications":false,"profile_link_color":"FF0000","default_profile_image":false,"utc_offset":null,"friends_count":144,"description":"Kareoke queen, huge fan of all things social media, football crazy, and a very loyal to my friends","profile_background_color":"642D8B","is_translator":false,"statuses_count":31,"profile_background_tile":true,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/361652875\/33045507467241-CharcoalDamask.jpg","favourites_count":0,"profile_sidebar_fill_color":"7AC3EE","location":"","follow_request_sent":false,"lang":"en","geo_enabled":false,"screen_name":"conchitaahgalli","profile_sidebar_border_color":"65B0DA","show_all_inline_media":false,"profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1632165317\/2008759090xrtujeujh_normal.jpg","id":282017664,"listed_count":0,"verified":false,"profile_use_background_image":true},"entities":{"urls":[],"hashtags":[],"user_mentions":[]},"retweeted":false,"truncated":false,"source":"web","id":144179660362752000,"text":"I detest going to the garage to get my car fixed, I always end up getting ripped off. :("},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:24 +0000 2011","user":{"profile_link_color":"ff6eff","protected":false,"default_profile_image":false,"following":true,"created_at":"Thu Oct 07 21:16:47 +0000 2010","friends_count":302,"name":"\ue12f\ue12fYC\ue12f\ue12f","notifications":false,"profile_background_color":"000000","is_translator":false,"statuses_count":2990,"profile_background_tile":true,"utc_offset":-18000,"description":"For Flyers,Cd Covers,Logo's,Posters,& etc. Email me at ryn_richmond@yahoo.com #TeamHIM #TeamSelfMade #TeamPaperChaser","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/367539460\/TWITTER.jpg","favourites_count":4,"profile_sidebar_fill_color":"000000","follow_request_sent":true,"geo_enabled":false,"profile_sidebar_border_color":"9e9da3","location":"Rhode\ue12fTo\ue12fRiches","show_all_inline_media":false,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1677869109\/image_normal.jpg","screen_name":"SELF_MADE_HIM","listed_count":0,"verified":false,"profile_use_background_image":true,"time_zone":"Quito","profile_text_color":"a8a3a3","id":199841786,"id_str":"199841786","contributors_enabled":false,"profile_background_image_url":"http:\/\/a2.twimg.com\/profile_background_images\/367539460\/TWITTER.jpg","followers_count":320,"profile_image_url":"http:\/\/a2.twimg.com\/profile_images\/1677869109\/image_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[]},"source":"\u003Ca href=\"http:\/\/twitter.com\/#!\/download\/iphone\" rel=\"nofollow\"\u003ETwitter for iPhone\u003C\/a\u003E","id":144179660144640000,"id_str":"144179660144640000","text":"I tell em fall back cuz if u hate u get put in a box quick"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:24 +0000 2011","user":{"profile_link_color":"B40B43","protected":false,"default_profile_image":false,"following":false,"created_at":"Sat Jun 04 18:27:50 +0000 2011","friends_count":125,"name":"\u2665Sexy Chica\u2665","notifications":false,"profile_background_color":"FF6699","is_translator":false,"statuses_count":3028,"profile_background_tile":true,"utc_offset":-21600,"description":"I'm a beautiful, independent, hard-working young woman....often imitated but never duplicated ;) ","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme11\/bg.gif","favourites_count":7,"profile_sidebar_fill_color":"E5507E","follow_request_sent":false,"geo_enabled":false,"profile_sidebar_border_color":"CC3366","location":"\u2606Land of the Five Star Chic\u2606","show_all_inline_media":false,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1493619085\/IMG-20110813-00178_normal.jpg","screen_name":"_MzYella","listed_count":2,"verified":false,"profile_use_background_image":true,"time_zone":"Central Time (US & Canada)","profile_text_color":"362720","id":310996869,"id_str":"310996869","contributors_enabled":false,"profile_background_image_url":"http:\/\/a1.twimg.com\/images\/themes\/theme11\/bg.gif","followers_count":128,"profile_image_url":"http:\/\/a2.twimg.com\/profile_images\/1493619085\/IMG-20110813-00178_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[]},"source":"\u003Ca href=\"http:\/\/blackberry.com\/twitter\" rel=\"nofollow\"\u003ETwitter for BlackBerry\u00ae\u003C\/a\u003E","id":144179659096064000,"id_str":"144179659096064000","text":"Just had a heart to heart with my son..damn near had me in tears"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:23 +0000 2011","possibly_sensitive":false,"user":{"profile_link_color":"FF0000","protected":false,"default_profile_image":false,"following":true,"created_at":"Sat Aug 20 14:36:52 +0000 2011","friends_count":1985,"name":"\u2588\u2764END\u2764THE\u2764WARS\u2764\u2588 ","notifications":false,"profile_background_color":"BADFCD","is_translator":false,"statuses_count":8812,"profile_background_tile":true,"utc_offset":-18000,"description":"\u25ba\u273f\u2122#OWS #TFB\u2122\u273f\u25baEND THE PATRIOT ACT\u2714 END THE FED\u2714 RE-ENACT THE GLASS STEAGALL ACT\u2714 INTRODUCE INTEREST FREE ISSUED MONEY\u2714 END OBAMA NIGHTMARE\u2714 \u255a\u25ba(\u273f\u25e0\u203f\u25e0)\/ \t\\(\u25d5\u203f\u25d5\u273f) ","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/343771946\/a_debt_free_world_2012.jpg","favourites_count":397,"profile_sidebar_fill_color":"FFF7CC","follow_request_sent":true,"geo_enabled":false,"profile_sidebar_border_color":"F2E195","location":"Demand Open Source Governance\u2714","show_all_inline_media":true,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1588061277\/BANKER_normal.jpg","screen_name":"Vote_Online","listed_count":33,"verified":false,"profile_use_background_image":true,"url":"http:\/\/www.usdebtclock.org\/","time_zone":"Quito","profile_text_color":"0C3E53","id":358799866,"id_str":"358799866","contributors_enabled":false,"profile_background_image_url":"http:\/\/a2.twimg.com\/profile_background_images\/343771946\/a_debt_free_world_2012.jpg","followers_count":1776,"profile_image_url":"http:\/\/a2.twimg.com\/profile_images\/1588061277\/BANKER_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[{"expanded_url":"http:\/\/bit.ly\/sKu0uN","indices":[116,136],"url":"http:\/\/t.co\/kSQGfZqb","display_url":"bit.ly\/sKu0uN"}],"hashtags":[],"user_mentions":[{"name":"\u2588\u2764END\u2764THE\u2764WARS\u2764\u2588 ","indices":[3,15],"screen_name":"Vote_Online","id":358799866,"id_str":"358799866"}]},"source":"\u003Ca href=\"http:\/\/twitterfeed.com\" rel=\"nofollow\"\u003Etwitterfeed\u003C\/a\u003E","id":144179657959424000,"id_str":"144179657959424000","text":"Rt @Vote_Online What If Donald Trump Held a Debate But the Candidates Didn't Show up? [Donald Trump]: \n\t\t\t\t\t\t\t\t\t... http:\/\/t.co\/kSQGfZqb"},{"id_str":"144179656801792000","created_at":"Tue Dec 06 22:21:24 +0000 2011","retweet_count":0,"favorited":false,"possibly_sensitive":false,"user":{"time_zone":"Central Time (US & Canada)","profile_text_color":"141314","protected":false,"id_str":"39401120","contributors_enabled":false,"following":false,"profile_background_image_url":"http:\/\/a2.twimg.com\/profile_background_images\/367790270\/delores.jpeg","created_at":"Tue May 12 01:40:40 +0000 2009","followers_count":493,"profile_image_url":"http:\/\/a1.twimg.com\/profile_images\/1644064362\/Aeei1NuCMAEVzuq__1__normal.jpg","name":"William Wildfire","default_profile":false,"notifications":false,"profile_link_color":"0a0a0a","default_profile_image":false,"utc_offset":-21600,"friends_count":331,"description":"i fantasized about this back in Chicago...","profile_background_color":"9AE4E8","is_translator":false,"statuses_count":19221,"profile_background_tile":true,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/367790270\/delores.jpeg","favourites_count":17,"profile_sidebar_fill_color":"faeb41","location":"Harvey,IL\/ Howard U","follow_request_sent":false,"lang":"en","geo_enabled":true,"screen_name":"tripleOGstatus","profile_sidebar_border_color":"122aff","url":"http:\/\/chickengofor30.tumblr.com\/","show_all_inline_media":false,"profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1644064362\/Aeei1NuCMAEVzuq__1__normal.jpg","id":39401120,"listed_count":2,"verified":false,"profile_use_background_image":true},"entities":{"urls":[],"hashtags":[],"media":[{"type":"photo","display_url":"pic.twitter.com\/AQUh4MjM","id_str":"144179656805986304","media_url_https":"https:\/\/p.twimg.com\/AgA6okvCMAAmcvI.jpg","indices":[2,22],"expanded_url":"http:\/\/twitter.com\/tripleOGstatus\/status\/144179656801792000\/photo\/1","url":"http:\/\/t.co\/AQUh4MjM","id":144179656805986304,"media_url":"http:\/\/p.twimg.com\/AgA6okvCMAAmcvI.jpg","sizes":{"small":{"h":455,"w":340,"resize":"fit"},"large":{"h":648,"w":484,"resize":"fit"},"thumb":{"h":150,"w":150,"resize":"crop"},"medium":{"h":648,"w":484,"resize":"fit"}}}],"user_mentions":[]},"retweeted":false,"truncated":false,"source":"\u003Ca href=\"http:\/\/www.apple.com\" rel=\"nofollow\"\u003EPhotos on iOS\u003C\/a\u003E","id":144179656801792000,"text":"B http:\/\/t.co\/AQUh4MjM"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:23 +0000 2011","user":{"profile_link_color":"a1092c","protected":false,"default_profile_image":false,"following":true,"created_at":"Sat Apr 18 04:03:20 +0000 2009","friends_count":325,"name":"stephanie soto","notifications":false,"profile_background_color":"f5f0f0","is_translator":false,"statuses_count":15526,"profile_background_tile":true,"utc_offset":-28800,"description":"' baddest women in the heart of the south ' - drake . \r\nbiochem major -future pharm school student\r\nBLESSED . GIFTED && THANKFUL ","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/376725554\/tumblr_lu3prpkyxi1qeg9v4o1_500.png","favourites_count":10,"profile_sidebar_fill_color":"855d5e","follow_request_sent":true,"geo_enabled":true,"profile_sidebar_border_color":"000000","location":"tx born&raised UofAZ student ","show_all_inline_media":true,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1608460179\/Photo_on_2011-10-26_at_16.05_normal.jpg","screen_name":"MiSS_SOTO","listed_count":4,"verified":false,"profile_use_background_image":true,"url":"http:\/\/miss-soto.tumblr.com\/","time_zone":"Pacific Time (US & Canada)","profile_text_color":"db0d3d","id":32773999,"id_str":"32773999","contributors_enabled":false,"profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/376725554\/tumblr_lu3prpkyxi1qeg9v4o1_500.png","followers_count":585,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1608460179\/Photo_on_2011-10-26_at_16.05_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[],"user_mentions":[]},"source":"\u003Ca href=\"http:\/\/levelupstudio.com\" rel=\"nofollow\"\u003EPlume\u00a0\u00a0\u003C\/a\u003E","id":144179656474624000,"id_str":"144179656474624000","text":"Hope you all are having a blessed day! : )"},{"retweet_count":0,"favorited":false,"created_at":"Tue Dec 06 22:21:23 +0000 2011","user":{"profile_link_color":"4beb3d","protected":false,"default_profile_image":false,"following":true,"created_at":"Mon Feb 28 05:23:44 +0000 2011","friends_count":73,"name":"Lourdes Tyla Sagum","notifications":false,"profile_background_color":"0a0a0a","is_translator":false,"statuses_count":229,"profile_background_tile":true,"utc_offset":14400,"description":"Write it on your heart that every day is the best day in the year.Keep love in your heart. A life without it is like a sunless garden when the flowers are dead.","default_profile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/211200950\/don_022.jpg","favourites_count":4,"profile_sidebar_fill_color":"114513","follow_request_sent":true,"geo_enabled":true,"profile_sidebar_border_color":"22f2be","location":"Philippines","show_all_inline_media":true,"lang":"en","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1589730468\/image_normal.jpg","screen_name":"tylabayot","listed_count":0,"verified":false,"profile_use_background_image":true,"time_zone":"Abu Dhabi","profile_text_color":"0c9e11","id":258657307,"id_str":"258657307","contributors_enabled":false,"profile_background_image_url":"http:\/\/a3.twimg.com\/profile_background_images\/211200950\/don_022.jpg","followers_count":36,"profile_image_url":"http:\/\/a2.twimg.com\/profile_images\/1589730468\/image_normal.jpg"},"retweeted":false,"truncated":false,"entities":{"urls":[],"hashtags":[{"indices":[0,9],"text":"highnoon"},{"indices":[10,20],"text":"ninjasaga"}],"user_mentions":[{"name":"Jamich","indices":[25,37],"screen_name":"ilovejamich","id":316296994,"id_str":"316296994"}]},"source":"\u003Ca href=\"http:\/\/twitter.com\/#!\/download\/iphone\" rel=\"nofollow\"\u003ETwitter for iPhone\u003C\/a\u003E","id":144179655535104000,"id_str":"144179655535104000","text":"#highnoon #ninjasaga :)) @ilovejamich naman :)) &lt;3"},{"id_str":"144179654813696000","created_at":"Tue Dec 06 22:21:23 +0000 2011","retweet_count":0,"favorited":false,"user":{"time_zone":"Pacific Time (US & Canada)","profile_text_color":"000000","protected":false,"id_str":"332456750","contributors_enabled":false,"following":false,"profile_background_image_url":"http:\/\/a0.twimg.com\/images\/themes\/theme1\/bg.png","created_at":"Sat Jul 09 21:01:43 +0000 2011","followers_count":90,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1666868564\/6H7uSFpa_normal","name":"Bryana LeBlanc","default_profile":false,"notifications":false,"profile_link_color":"ff0099","default_profile_image":false,"utc_offset":-28800,"friends_count":145,"description":"You Dont Need To Know Shyt Abt Me x] K? :) Thanks :D","profile_background_color":"000000","is_translator":false,"statuses_count":1036,"profile_background_tile":false,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme1\/bg.png","favourites_count":4,"profile_sidebar_fill_color":"4a464a","location":"CaL~E","follow_request_sent":false,"lang":"en","geo_enabled":true,"screen_name":"iBreBanggin","profile_sidebar_border_color":"000000","url":"http:\/\/www.facebook.com\/?ref=home#!\/PeaNana","show_all_inline_media":false,"profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1666868564\/6H7uSFpa_normal","id":332456750,"listed_count":0,"verified":false,"profile_use_background_image":false},"entities":{"urls":[],"hashtags":[],"user_mentions":[]},"retweeted":false,"truncated":false,"source":"\u003Ca href=\"http:\/\/twitter.com\/download\/android\" rel=\"nofollow\"\u003ETwitter for Android\u003C\/a\u003E","id":144179654813696000,"text":"The Questions Not Why, The Questions Why Not ??"},{"id_str":"144179654289408000","created_at":"Tue Dec 06 22:21:22 +0000 2011","retweet_count":0,"favorited":false,"user":{"time_zone":"Santiago","profile_text_color":"333333","protected":false,"id_str":"288936501","contributors_enabled":false,"following":false,"profile_background_image_url":"http:\/\/a1.twimg.com\/images\/themes\/theme14\/bg.gif","created_at":"Wed Apr 27 18:25:37 +0000 2011","followers_count":52,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1328611258\/Foto_032711_001_normal.jpg","name":"Anderson","default_profile":false,"notifications":false,"profile_link_color":"009999","default_profile_image":false,"utc_offset":-14400,"friends_count":123,"description":"","profile_background_color":"131516","is_translator":false,"statuses_count":595,"profile_background_tile":true,"profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme14\/bg.gif","favourites_count":0,"profile_sidebar_fill_color":"efefef","location":"guarulhos","follow_request_sent":false,"lang":"pt","geo_enabled":false,"screen_name":"andinho_verdao","profile_sidebar_border_color":"eeeeee","show_all_inline_media":false,"profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1328611258\/Foto_032711_001_normal.jpg","id":288936501,"listed_count":0,"verified":false,"profile_use_background_image":true},"entities":{"urls":[],"hashtags":[{"indices":[0,23],"text":"euvoutentandoteagarrar"}],"user_mentions":[]},"retweeted":false,"truncated":false,"source":"web","id":144179654289408000,"text":"#euvoutentandoteagarrar gustavo lima"}],"search_metadata": { "completed_in": 0.087, "max_id": 505874924095815700, "max_id_str": "505874924095815681", "next_results": "?max_id=505874847260352512&q=%E4%B8%80&count=100&include_entities=1", "query": "%E4%B8%80", "refresh_url": "?since_id=505874924095815681&q=%E4%B8%80&include_entities=1", "count": 100, "since_id": 0, "since_id_str": "0" }}
diff --git a/benchmark/src/jmh/resources/twitter_macro.json b/benchmark/src/jmh/resources/twitter_macro.json
new file mode 100644
index 00000000..137fb516
--- /dev/null
+++ b/benchmark/src/jmh/resources/twitter_macro.json
@@ -0,0 +1,15482 @@
+{
+ "statuses": [
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:15 +0000 2014",
+ "id": 505874924095815700,
+ "id_str": "505874924095815681",
+ "text": "@aym0566x \n\nåå‰:å‰ç”°ã‚ゆã¿\n第一å°è±¡:ãªã‚“ã‹æ€–ã£ï¼\n今ã®å°è±¡:ã¨ã‚Šã‚ãˆãšã‚­ãƒ¢ã„。噛ã¿åˆã‚ãªã„\n好ããªã¨ã“ã‚:ã¶ã™ã§ã‚­ãƒ¢ã„ã¨ã“😋✨✨\næ€ã„出:んーーーã€ã‚ã‚Šã™ãŽðŸ˜Šâ¤ï¸\nLINE交æ›ã§ãる?:ã‚ã……ã”ã‚ん✋\nトプ画をã¿ã¦:照れã¾ã™ãŒãªðŸ˜˜âœ¨\n一言:ãŠå‰ã¯ä¸€ç”Ÿã‚‚ã‚“ã®ãƒ€ãƒðŸ’–",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": 866260188,
+ "in_reply_to_user_id_str": "866260188",
+ "in_reply_to_screen_name": "aym0566x",
+ "user": {
+ "id": 1186275104,
+ "id_str": "1186275104",
+ "name": "AYUMI",
+ "screen_name": "ayuu0123",
+ "location": "",
+ "description": "元野çƒéƒ¨ãƒžãƒãƒ¼ã‚¸ãƒ£ãƒ¼â¤ï¸Žâ€¦æœ€é«˜ã®å¤ã‚’ã‚ã‚ŠãŒã¨ã†â€¦â¤ï¸Ž",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 262,
+ "friends_count": 252,
+ "listed_count": 0,
+ "created_at": "Sat Feb 16 13:40:25 +0000 2013",
+ "favourites_count": 235,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 1769,
+ "lang": "en",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/497760886795153410/LDjAwR_y_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/497760886795153410/LDjAwR_y_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1186275104/1409318784",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "aym0566x",
+ "name": "å‰ç”°ã‚ゆã¿",
+ "id": 866260188,
+ "id_str": "866260188",
+ "indices": [
+ 0,
+ 9
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:14 +0000 2014",
+ "id": 505874922023837700,
+ "id_str": "505874922023837696",
+ "text": "RT @KATANA77: ãˆã£ãã‚Œã¯ãƒ»ãƒ»ãƒ»ï¼ˆä¸€åŒï¼‰ http://t.co/PkCJAcSuYK",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 903487807,
+ "id_str": "903487807",
+ "name": "RT&ファボ魔ã®ã‚€ã£ã¤ã‚“ã•ã£m",
+ "screen_name": "yuttari1998",
+ "location": "関西 ↓詳ã—ã„プロ↓",
+ "description": "無言フォローã¯ã‚ã¾ã‚Šå¥½ã¿ã¾ã›ã‚“ ゲームã¨å‹•ç”»ãŒå¥½ãã§ã™ã‚·ãƒ¢é‡ŽéƒŽã§ã™ãŒã‚ˆã‚ã—ã…最近ã¯MGSã¨ãƒ–レイブルーã€éŸ³ã‚²ãƒ¼ã‚’プレイã—ã¦ã¾ã™",
+ "url": "http://t.co/Yg9e1Fl8wd",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/Yg9e1Fl8wd",
+ "expanded_url": "http://twpf.jp/yuttari1998",
+ "display_url": "twpf.jp/yuttari1998",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 95,
+ "friends_count": 158,
+ "listed_count": 1,
+ "created_at": "Thu Oct 25 08:27:13 +0000 2012",
+ "favourites_count": 3652,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 10276,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/500268849275494400/AoXHZ7Ij_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/500268849275494400/AoXHZ7Ij_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/903487807/1409062272",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sat Aug 30 23:49:35 +0000 2014",
+ "id": 505864943636197400,
+ "id_str": "505864943636197376",
+ "text": "ãˆã£ãã‚Œã¯ãƒ»ãƒ»ãƒ»ï¼ˆä¸€åŒï¼‰ http://t.co/PkCJAcSuYK",
+ "source": "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 77915997,
+ "id_str": "77915997",
+ "name": "(有)刀",
+ "screen_name": "KATANA77",
+ "location": "",
+ "description": "プリキュア好ãã®ã‚µãƒ©ãƒªãƒ¼ãƒžãƒ³ã§ã™ã€‚好ããªãƒ—リキュアシリーズã¯ãƒãƒ¼ãƒˆã‚­ãƒ£ãƒƒãƒã€æœ€æ„›ã®ã‚­ãƒ£ãƒ©ã‚¯ã‚¿ãƒ¼ã¯æœˆå½±ã‚†ã‚Šã•ã‚“ã§ã™ã€‚ http://t.co/QMLJeFmfMTã”質å•ã€ãŠå•ã„åˆã‚ã›ã¯ã“ã¡ã‚‰ http://t.co/LU8T7vmU3h",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": [
+ {
+ "url": "http://t.co/QMLJeFmfMT",
+ "expanded_url": "http://www.pixiv.net/member.php?id=4776",
+ "display_url": "pixiv.net/member.php?id=…",
+ "indices": [
+ 58,
+ 80
+ ]
+ },
+ {
+ "url": "http://t.co/LU8T7vmU3h",
+ "expanded_url": "http://ask.fm/KATANA77",
+ "display_url": "ask.fm/KATANA77",
+ "indices": [
+ 95,
+ 117
+ ]
+ }
+ ]
+ }
+ },
+ "protected": false,
+ "followers_count": 1095,
+ "friends_count": 740,
+ "listed_count": 50,
+ "created_at": "Mon Sep 28 03:41:27 +0000 2009",
+ "favourites_count": 3741,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": true,
+ "verified": false,
+ "statuses_count": 19059,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/808597451/45b82f887085d32bd4b87dfc348fe22a.png",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/808597451/45b82f887085d32bd4b87dfc348fe22a.png",
+ "profile_background_tile": true,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/480210114964504577/MjVIEMS4_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/480210114964504577/MjVIEMS4_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/77915997/1404661392",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "FFFFFF",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 82,
+ "favorite_count": 42,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [],
+ "media": [
+ {
+ "id": 505864942575034400,
+ "id_str": "505864942575034369",
+ "indices": [
+ 13,
+ 35
+ ],
+ "media_url": "http://pbs.twimg.com/media/BwUxfC6CIAEr-Ye.jpg",
+ "media_url_https": "https://pbs.twimg.com/media/BwUxfC6CIAEr-Ye.jpg",
+ "url": "http://t.co/PkCJAcSuYK",
+ "display_url": "pic.twitter.com/PkCJAcSuYK",
+ "expanded_url": "http://twitter.com/KATANA77/status/505864943636197376/photo/1",
+ "type": "photo",
+ "sizes": {
+ "medium": {
+ "w": 600,
+ "h": 338,
+ "resize": "fit"
+ },
+ "small": {
+ "w": 340,
+ "h": 192,
+ "resize": "fit"
+ },
+ "thumb": {
+ "w": 150,
+ "h": 150,
+ "resize": "crop"
+ },
+ "large": {
+ "w": 765,
+ "h": 432,
+ "resize": "fit"
+ }
+ }
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ "retweet_count": 82,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "KATANA77",
+ "name": "(有)刀",
+ "id": 77915997,
+ "id_str": "77915997",
+ "indices": [
+ 3,
+ 12
+ ]
+ }
+ ],
+ "media": [
+ {
+ "id": 505864942575034400,
+ "id_str": "505864942575034369",
+ "indices": [
+ 27,
+ 49
+ ],
+ "media_url": "http://pbs.twimg.com/media/BwUxfC6CIAEr-Ye.jpg",
+ "media_url_https": "https://pbs.twimg.com/media/BwUxfC6CIAEr-Ye.jpg",
+ "url": "http://t.co/PkCJAcSuYK",
+ "display_url": "pic.twitter.com/PkCJAcSuYK",
+ "expanded_url": "http://twitter.com/KATANA77/status/505864943636197376/photo/1",
+ "type": "photo",
+ "sizes": {
+ "medium": {
+ "w": 600,
+ "h": 338,
+ "resize": "fit"
+ },
+ "small": {
+ "w": 340,
+ "h": 192,
+ "resize": "fit"
+ },
+ "thumb": {
+ "w": 150,
+ "h": 150,
+ "resize": "crop"
+ },
+ "large": {
+ "w": 765,
+ "h": 432,
+ "resize": "fit"
+ }
+ },
+ "source_status_id": 505864943636197400,
+ "source_status_id_str": "505864943636197376"
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:14 +0000 2014",
+ "id": 505874920140591100,
+ "id_str": "505874920140591104",
+ "text": "@longhairxMIURA æœä¸€ãƒ©ã‚¤ã‚«ã‚¹è¾›ç›®ã ã‚ˆw",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": 505874728897085440,
+ "in_reply_to_status_id_str": "505874728897085440",
+ "in_reply_to_user_id": 114188950,
+ "in_reply_to_user_id_str": "114188950",
+ "in_reply_to_screen_name": "longhairxMIURA",
+ "user": {
+ "id": 114786346,
+ "id_str": "114786346",
+ "name": "PROTECT-T",
+ "screen_name": "ttm_protect",
+ "location": "é™å²¡çœŒé•·æ³‰ç”º",
+ "description": "24 / XXX / @andprotector / @lifefocus0545 potato design works",
+ "url": "http://t.co/5EclbQiRX4",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/5EclbQiRX4",
+ "expanded_url": "http://ap.furtherplatonix.net/index.html",
+ "display_url": "ap.furtherplatonix.net/index.html",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 1387,
+ "friends_count": 903,
+ "listed_count": 25,
+ "created_at": "Tue Feb 16 16:13:41 +0000 2010",
+ "favourites_count": 492,
+ "utc_offset": 32400,
+ "time_zone": "Osaka",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 12679,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/481360383253295104/4B9Rcfys_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/481360383253295104/4B9Rcfys_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/114786346/1403600232",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "longhairxMIURA",
+ "name": "miura desu",
+ "id": 114188950,
+ "id_str": "114188950",
+ "indices": [
+ 0,
+ 15
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:14 +0000 2014",
+ "id": 505874919020699650,
+ "id_str": "505874919020699648",
+ "text": "RT @omo_kko: ラウワン脱出→å‹é”ãŒå®¶ã«é€£ã‚“ã§å¸°ã£ã¦ã£ã¦è¨€ã†ã‹ã‚‰å‹é”ん家ã«ä¹—ã›ã¦å¸°ã‚‹(1度も行ã£ãŸã“ã¨ãªã„田舎é“)→å‹é”ãŠã‚ã—ã¦è¿·å­â†’500メートルãらã„続ã変ãªä¸€æœ¬é“進む→墓地ã§è¡Œãæ­¢ã¾ã‚Šã§Uターン出æ¥ãšãƒãƒƒã‚¯ã§500メートル元ã®ã¨ã“ã‚ã¾ã§é€²ã¾ãªã„ã¨ã„ã‘ãªã„â†ä»Šã“ã“",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 392585658,
+ "id_str": "392585658",
+ "name": "原稿",
+ "screen_name": "chibu4267",
+ "location": "キミã®éƒ¨å±‹ã®ç‡ƒãˆã‚‹ã‚´ãƒŸç®±",
+ "description": "RTã—ã¦TLã«æ¿æµã‚’èµ·ã“ã™ã‹ã‚‰ãƒ•ã‚©ãƒ­ãƒ¼ã—ãªã„æ–¹ãŒè‰¯ã„よ 言ã£ã¦ã‚‹ã“ã¨ã‚‚ã¤ã¾ã‚‰ãªã„㗠詳細→http://t.co/ANSFlYXERJ 相方@1life_5106_hshd 葛西教徒ãã®å£±",
+ "url": "http://t.co/JTFjV89eaN",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/JTFjV89eaN",
+ "expanded_url": "http://www.pixiv.net/member.php?id=1778417",
+ "display_url": "pixiv.net/member.php?id=…",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": [
+ {
+ "url": "http://t.co/ANSFlYXERJ",
+ "expanded_url": "http://twpf.jp/chibu4267",
+ "display_url": "twpf.jp/chibu4267",
+ "indices": [
+ 45,
+ 67
+ ]
+ }
+ ]
+ }
+ },
+ "protected": false,
+ "followers_count": 1324,
+ "friends_count": 1165,
+ "listed_count": 99,
+ "created_at": "Mon Oct 17 08:23:46 +0000 2011",
+ "favourites_count": 9542,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": true,
+ "verified": false,
+ "statuses_count": 369420,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/453106940822814720/PcJIZv43.png",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/453106940822814720/PcJIZv43.png",
+ "profile_background_tile": true,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/505731759216943107/pzhnkMEg_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/505731759216943107/pzhnkMEg_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/392585658/1362383911",
+ "profile_link_color": "5EB9FF",
+ "profile_sidebar_border_color": "FFFFFF",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sat Aug 30 16:51:09 +0000 2014",
+ "id": 505759640164892700,
+ "id_str": "505759640164892673",
+ "text": "ラウワン脱出→å‹é”ãŒå®¶ã«é€£ã‚“ã§å¸°ã£ã¦ã£ã¦è¨€ã†ã‹ã‚‰å‹é”ん家ã«ä¹—ã›ã¦å¸°ã‚‹(1度も行ã£ãŸã“ã¨ãªã„田舎é“)→å‹é”ãŠã‚ã—ã¦è¿·å­â†’500メートルãらã„続ã変ãªä¸€æœ¬é“進む→墓地ã§è¡Œãæ­¢ã¾ã‚Šã§Uターン出æ¥ãšãƒãƒƒã‚¯ã§500メートル元ã®ã¨ã“ã‚ã¾ã§é€²ã¾ãªã„ã¨ã„ã‘ãªã„â†ä»Šã“ã“",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 309565423,
+ "id_str": "309565423",
+ "name": "ãŠã‚‚ã£ã“",
+ "screen_name": "omo_kko",
+ "location": "",
+ "description": "ã±ã‚“ã™ã¨",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 730,
+ "friends_count": 200,
+ "listed_count": 23,
+ "created_at": "Thu Jun 02 09:15:51 +0000 2011",
+ "favourites_count": 5441,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": true,
+ "verified": false,
+ "statuses_count": 30012,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/499126939378929664/GLWpIKTW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/499126939378929664/GLWpIKTW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/309565423/1409418370",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 67,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "omo_kko",
+ "name": "ãŠã‚‚ã£ã“",
+ "id": 309565423,
+ "id_str": "309565423",
+ "indices": [
+ 3,
+ 11
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:13 +0000 2014",
+ "id": 505874918198624260,
+ "id_str": "505874918198624256",
+ "text": "RT @thsc782_407: #LEDカツカツé¸æ‰‹æ¨©\n漢字一文字ã¶ã‚“ã®ã‚¹ãƒšãƒ¼ã‚¹ã«ã€Œãƒã‚¦ã‚¹ãƒ†ãƒ³ãƒœã‚¹ã€ã‚’åŽã‚ã‚‹ç‹‚æ°— http://t.co/vmrreDMziI",
+ "source": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 753161754,
+ "id_str": "753161754",
+ "name": "ã­ã“ã­ã“ã¿ã‹ã‚“*",
+ "screen_name": "nekonekomikan",
+ "location": "ソーダ水ã®ã‚ãµã‚Œã‚‹ãƒ“ンã®ä¸­",
+ "description": "猫×6ã€å¤§å­¦ãƒ»é«˜æ ¡ãƒ»æ—¦é‚£å„1ã¨æš®ã‚‰ã—ã¦ã„ã¾ã™ã€‚猫ã€å­ä¾›ã€æ—¥å¸¸æ€ã£ãŸäº‹ã‚’ã¤ã¶ã‚„ã„ã¦ã„ã¾ã™ï¼ä»Šå¹´ã®ç›®æ¨™ï¼šèª­æ›¸ã€åº­ã®æ‰‹å…¥ã‚Œã€ãƒ©ãƒ³ãƒ‹ãƒ³ã‚°ã€æ‰‹èŠ¸ï¼çŒ«ï¼ŠèŠ±ï¼Šå†™çœŸï¼Šè©©ï¼Šæž—ã‚‚ã‚‚ã“ã•ã‚“*鉄é“ãªã©å¥½ããªæ–¹ã‚’フォローã•ã›ã¦ã„ãŸã ã„ã¦ã„ã¾ã™ã€‚よã‚ã—ããŠé¡˜ã„ã—ã¾ã™â™¬",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 217,
+ "friends_count": 258,
+ "listed_count": 8,
+ "created_at": "Sun Aug 12 14:00:47 +0000 2012",
+ "favourites_count": 7650,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 20621,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/470627990271848448/m83uy6Vc_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/470627990271848448/m83uy6Vc_normal.jpeg",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Fri Feb 28 16:04:13 +0000 2014",
+ "id": 439430848190742500,
+ "id_str": "439430848190742528",
+ "text": "#LEDカツカツé¸æ‰‹æ¨©\n漢字一文字ã¶ã‚“ã®ã‚¹ãƒšãƒ¼ã‚¹ã«ã€Œãƒã‚¦ã‚¹ãƒ†ãƒ³ãƒœã‚¹ã€ã‚’åŽã‚ã‚‹ç‹‚æ°— http://t.co/vmrreDMziI",
+ "source": "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 82900665,
+ "id_str": "82900665",
+ "name": "[90]é’è‘‰å° èŠ¦ (第二粟屋) 屋",
+ "screen_name": "thsc782_407",
+ "location": "ã‹ã‚“ã¾ã—ã",
+ "description": "湯ã®è¡—ã®å…ƒå‹ƒé…©å§¦ãªã‚“ã¡ã‚ƒã‚‰å¤§ã€€èµ¤ã„犬ã®çŠ¬ï¼ˆå¤–資系) 肥後ã§ç·‘ナンãƒãƒ¼å±‹ã•ã‚“勤ã‚\nãã ã‚‰ãªã„ã“ã¨ã—ã‹ã¤ã¶ã‚„ã‹ãªã„ã—ã€ã„ã¡ã„ã¡è¨³ã®ã‚ã‹ã‚‰ãªã„記å·ã‚’連呼ã™ã‚‹ã®ã§ç›¸å½“邪魔ã«ãªã‚‹ã¨æ€ã„ã¾ã™ã€‚害ã¯ãªã„ã¨æ€ã„ã¾ã™ã€‚ã®ã‚Šã‚‚ã®ã®ç”»åƒã¨ã‹ãŸãã•ã‚“上ã’ã¾ã™ã€‚ã•ã¿ã—ã„。車輪ã®ã¤ã„ãŸã‚‚ã®ãªã‚‰ã ã„ãŸã„好ã。",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 587,
+ "friends_count": 623,
+ "listed_count": 30,
+ "created_at": "Fri Oct 16 15:13:32 +0000 2009",
+ "favourites_count": 1405,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": true,
+ "verified": false,
+ "statuses_count": 60427,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "352726",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/154137819/__813-1103.jpg",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/154137819/__813-1103.jpg",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/493760276676620289/32oLiTtT_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/493760276676620289/32oLiTtT_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/82900665/1398865798",
+ "profile_link_color": "D02B55",
+ "profile_sidebar_border_color": "829D5E",
+ "profile_sidebar_fill_color": "99CC33",
+ "profile_text_color": "3E4415",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 3291,
+ "favorite_count": 1526,
+ "entities": {
+ "hashtags": [
+ {
+ "text": "LEDカツカツé¸æ‰‹æ¨©",
+ "indices": [
+ 0,
+ 11
+ ]
+ }
+ ],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [],
+ "media": [
+ {
+ "id": 439430848194936800,
+ "id_str": "439430848194936832",
+ "indices": [
+ 41,
+ 63
+ ],
+ "media_url": "http://pbs.twimg.com/media/BhksBzoCAAAJeDS.jpg",
+ "media_url_https": "https://pbs.twimg.com/media/BhksBzoCAAAJeDS.jpg",
+ "url": "http://t.co/vmrreDMziI",
+ "display_url": "pic.twitter.com/vmrreDMziI",
+ "expanded_url": "http://twitter.com/thsc782_407/status/439430848190742528/photo/1",
+ "type": "photo",
+ "sizes": {
+ "medium": {
+ "w": 600,
+ "h": 450,
+ "resize": "fit"
+ },
+ "large": {
+ "w": 1024,
+ "h": 768,
+ "resize": "fit"
+ },
+ "thumb": {
+ "w": 150,
+ "h": 150,
+ "resize": "crop"
+ },
+ "small": {
+ "w": 340,
+ "h": 255,
+ "resize": "fit"
+ }
+ }
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ "retweet_count": 3291,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [
+ {
+ "text": "LEDカツカツé¸æ‰‹æ¨©",
+ "indices": [
+ 17,
+ 28
+ ]
+ }
+ ],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "thsc782_407",
+ "name": "[90]é’è‘‰å° èŠ¦ (第二粟屋) 屋",
+ "id": 82900665,
+ "id_str": "82900665",
+ "indices": [
+ 3,
+ 15
+ ]
+ }
+ ],
+ "media": [
+ {
+ "id": 439430848194936800,
+ "id_str": "439430848194936832",
+ "indices": [
+ 58,
+ 80
+ ],
+ "media_url": "http://pbs.twimg.com/media/BhksBzoCAAAJeDS.jpg",
+ "media_url_https": "https://pbs.twimg.com/media/BhksBzoCAAAJeDS.jpg",
+ "url": "http://t.co/vmrreDMziI",
+ "display_url": "pic.twitter.com/vmrreDMziI",
+ "expanded_url": "http://twitter.com/thsc782_407/status/439430848190742528/photo/1",
+ "type": "photo",
+ "sizes": {
+ "medium": {
+ "w": 600,
+ "h": 450,
+ "resize": "fit"
+ },
+ "large": {
+ "w": 1024,
+ "h": 768,
+ "resize": "fit"
+ },
+ "thumb": {
+ "w": 150,
+ "h": 150,
+ "resize": "crop"
+ },
+ "small": {
+ "w": 340,
+ "h": 255,
+ "resize": "fit"
+ }
+ },
+ "source_status_id": 439430848190742500,
+ "source_status_id_str": "439430848190742528"
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:13 +0000 2014",
+ "id": 505874918039228400,
+ "id_str": "505874918039228416",
+ "text": "ã€é‡‘一地区太鼓å°ã€‘å·é–¢ã¨å°å±±ã®è¦‹åˆ†ã‘ãŒã¤ã‹ãªã„",
+ "source": "<a href=\"http://twittbot.net/\" rel=\"nofollow\">twittbot.net</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2530194984,
+ "id_str": "2530194984",
+ "name": "å·ä¹‹æ±Ÿä¸­é«˜ç”Ÿã‚ã‚‹ã‚ã‚‹",
+ "screen_name": "kw_aru",
+ "location": "DMã«ã¦ãƒã‚¿æ供待ã£ã¦ã¾ã™ã‚ˆ",
+ "description": "å·ä¹‹æ±Ÿä¸­é«˜ç”Ÿã®å·ä¹‹æ±Ÿä¸­é«˜ç”Ÿã«ã‚ˆã‚‹å·ä¹‹æ±Ÿä¸­é«˜ç”Ÿã®ãŸã‚ã®ã‚ã‚‹ã‚るアカウントã§ã™ã€‚タイムリーãªãƒã‚¿ã¯ãŠæ°—ã«å…¥ã‚Šã«ã‚ã‚Šã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 113,
+ "friends_count": 157,
+ "listed_count": 0,
+ "created_at": "Wed May 28 15:01:43 +0000 2014",
+ "favourites_count": 30,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 4472,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/471668359314948097/XbIyXiZK_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/471668359314948097/XbIyXiZK_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2530194984/1401289473",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:13 +0000 2014",
+ "id": 505874915338104800,
+ "id_str": "505874915338104833",
+ "text": "ãŠã¯ã‚ˆã†ã”ã–ã„ã¾ã™ã‚“♪ SSDSã®DVDãŒæœä¸€ã§å±Šã„ãŸã€œï¼ˆâ‰§âˆ‡â‰¦ï¼‰",
+ "source": "<a href=\"http://tweetli.st/\" rel=\"nofollow\">TweetList!</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 428179337,
+ "id_str": "428179337",
+ "name": "サラ",
+ "screen_name": "sala_mgn",
+ "location": "æ±äº¬éƒ½",
+ "description": "botéŠã³ã¨å®Ÿæ³ãŒä¸»ç›®çš„ã®è¶£å‘³ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã€‚æˆäººæ¸ˆâ™€ã€‚時々TLãŠé¨’ãŒã›ã—ã¾ã™ã€‚リフォ率低ã„ã§ã™ãŒï¼¦ï¼ï¼¢ã”自由ã«ã€‚スパムã¯ãƒ–ロックï¼[HOT]K[アニメ]タイãƒãƒ‹/K/薄桜鬼/トライガン/進撃[å°èª¬]冲方ä¸/森åšå—£[漫画]内藤泰弘/高河ゆん[ä»–]声優/演劇 ※@sano_bot1二代目管ç†äºº",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 104,
+ "friends_count": 421,
+ "listed_count": 2,
+ "created_at": "Sun Dec 04 12:51:18 +0000 2011",
+ "favourites_count": 3257,
+ "utc_offset": -36000,
+ "time_zone": "Hawaii",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 25303,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "1A1B1F",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/601682567/put73jtg48ytjylq00if.jpeg",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/601682567/put73jtg48ytjylq00if.jpeg",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/3350624721/755920942e4f512e6ba489df7eb1147e_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/3350624721/755920942e4f512e6ba489df7eb1147e_normal.jpeg",
+ "profile_link_color": "2FC2EF",
+ "profile_sidebar_border_color": "181A1E",
+ "profile_sidebar_fill_color": "252429",
+ "profile_text_color": "666666",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:13 +0000 2014",
+ "id": 505874914897690600,
+ "id_str": "505874914897690624",
+ "text": "@ran_kirazuki ãã®ã‚ˆã†ãªãŠè¨€è‘‰ã‚’é ‚ã‘ã‚‹ã¨ã¯â€¦â€¦ï¼ã“ã®é›¨å¤ªéƒŽã€èª å¿ƒèª æ„ã‚’æŒã£ã¦å§‰å¾¡ã®è¶³ã®æŒ‡ã®ç¬¬ä¸€é–¢ç¯€ã‚’å´‡ã‚奉りã¨ã†ã”ã–ã„ã¾ã™",
+ "source": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
+ "truncated": false,
+ "in_reply_to_status_id": 505874276692406300,
+ "in_reply_to_status_id_str": "505874276692406272",
+ "in_reply_to_user_id": 531544559,
+ "in_reply_to_user_id_str": "531544559",
+ "in_reply_to_screen_name": "ran_kirazuki",
+ "user": {
+ "id": 2364828518,
+ "id_str": "2364828518",
+ "name": "雨",
+ "screen_name": "tear_dice",
+ "location": "変態/日常/創作/室町/ãŸã¾ã«ç‰ˆæ¨©",
+ "description": "アイコンã¯å…„ã•ã‚“ã‹ã‚‰ï¼",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 28,
+ "friends_count": 28,
+ "listed_count": 0,
+ "created_at": "Fri Feb 28 00:28:40 +0000 2014",
+ "favourites_count": 109,
+ "utc_offset": 32400,
+ "time_zone": "Seoul",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 193,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "000000",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/504434510675443713/lvW7ad5b.jpeg",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/504434510675443713/lvW7ad5b.jpeg",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/505170142284640256/rnW4XeEJ_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/505170142284640256/rnW4XeEJ_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2364828518/1409087198",
+ "profile_link_color": "0D31BF",
+ "profile_sidebar_border_color": "000000",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "ran_kirazuki",
+ "name": "蘭ã´ã‚ˆã®æ—¥å¸¸",
+ "id": 531544559,
+ "id_str": "531544559",
+ "indices": [
+ 0,
+ 13
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:13 +0000 2014",
+ "id": 505874914591514600,
+ "id_str": "505874914591514626",
+ "text": "RT @AFmbsk: @samao21718 \n呼ã³æ–¹â˜žã¾ãŠã¡ã‚ƒã‚“\n呼ã°ã‚Œæ–¹â˜žã‚ーã¡ã‚ƒã‚“\n第一å°è±¡â˜žå¹³é‡Žã‹ã‚‰ï¼Ÿï¼\n今ã®å°è±¡â˜žãŠã¨ãªã£ã½ã„ï¼ï¼\nLINE交æ›â˜žã‚‚ã£ã¦ã‚‹ã‚“\\( ˆoˆ )/\nトプ画ã«ã¤ã„ã¦â˜žæ¥½ã—ãã†ã§ã„ーãªðŸ˜³\n家æ—ã«ã™ã‚‹ãªã‚‰â˜žãŠã­ã‡ã¡ã‚ƒã‚“\n最後ã«ä¸€è¨€â˜žå…¨ç„¶ä¼šãˆãªã„…",
+ "source": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2179759316,
+ "id_str": "2179759316",
+ "name": "ã¾ãŠ",
+ "screen_name": "samao21718",
+ "location": "埼玉 UK留学ã—ã¦ã¾ã—ãŸâœˆ",
+ "description": "゚.*97line ãŠã•ã‚‰ã«è²¢ã„ã§ã‚‹ç³»å¥³å­ï¼Š.ã‚œ DISH// ✯ ä½é‡Žæ‚ æ–— ✯ 読モ ✯ WEGO ✯ åµ I met @OTYOfficial in the London ;)",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 111,
+ "friends_count": 121,
+ "listed_count": 0,
+ "created_at": "Thu Nov 07 09:47:41 +0000 2013",
+ "favourites_count": 321,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 1777,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501535615351926784/c5AAh6Sz_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501535615351926784/c5AAh6Sz_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2179759316/1407640217",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sat Aug 30 14:59:49 +0000 2014",
+ "id": 505731620456771600,
+ "id_str": "505731620456771584",
+ "text": "@samao21718 \n呼ã³æ–¹â˜žã¾ãŠã¡ã‚ƒã‚“\n呼ã°ã‚Œæ–¹â˜žã‚ーã¡ã‚ƒã‚“\n第一å°è±¡â˜žå¹³é‡Žã‹ã‚‰ï¼Ÿï¼\n今ã®å°è±¡â˜žãŠã¨ãªã£ã½ã„ï¼ï¼\nLINE交æ›â˜žã‚‚ã£ã¦ã‚‹ã‚“\\( ˆoˆ )/\nトプ画ã«ã¤ã„ã¦â˜žæ¥½ã—ãã†ã§ã„ーãªðŸ˜³\n家æ—ã«ã™ã‚‹ãªã‚‰â˜žãŠã­ã‡ã¡ã‚ƒã‚“\n最後ã«ä¸€è¨€â˜žå…¨ç„¶ä¼šãˆãªã„ã­ãƒ¼ä»Šåº¦ä¼šãˆãŸã‚‰ã„ã„ãªï¼",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": 2179759316,
+ "in_reply_to_user_id_str": "2179759316",
+ "in_reply_to_screen_name": "samao21718",
+ "user": {
+ "id": 1680668713,
+ "id_str": "1680668713",
+ "name": "★Shiiiii!☆",
+ "screen_name": "AFmbsk",
+ "location": "埼玉",
+ "description": "2310*basketball#41*UVERworld*Pooh☪Bell +.。*å¼±ã•ã‚’知ã£ã¦å¼·ããªã‚Œ*゚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 429,
+ "friends_count": 434,
+ "listed_count": 0,
+ "created_at": "Sun Aug 18 12:45:00 +0000 2013",
+ "favourites_count": 2488,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 6352,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/504643170886365185/JN_dlwUd_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/504643170886365185/JN_dlwUd_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1680668713/1408805886",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 1,
+ "favorite_count": 1,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "samao21718",
+ "name": "ã¾ãŠ",
+ "id": 2179759316,
+ "id_str": "2179759316",
+ "indices": [
+ 0,
+ 11
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 1,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "AFmbsk",
+ "name": "★Shiiiii!☆",
+ "id": 1680668713,
+ "id_str": "1680668713",
+ "indices": [
+ 3,
+ 10
+ ]
+ },
+ {
+ "screen_name": "samao21718",
+ "name": "ã¾ãŠ",
+ "id": 2179759316,
+ "id_str": "2179759316",
+ "indices": [
+ 12,
+ 23
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:10 +0000 2014",
+ "id": 505874905712189440,
+ "id_str": "505874905712189440",
+ "text": "一ã€å¸¸ã«èº«ä¸€ã¤ç°¡ç´ ã«ã—ã¦ã€ç¾Žé£Ÿã‚’好んã§ã¯ãªã‚‰ãªã„",
+ "source": "<a href=\"http://twittbot.net/\" rel=\"nofollow\">twittbot.net</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1330420010,
+ "id_str": "1330420010",
+ "name": "ç¨è¡Œé“bot",
+ "screen_name": "dokkodo_bot",
+ "location": "",
+ "description": "宮本武蔵ã®è‡ªèª“書ã€ã€Œç¨è¡Œé“ã€ã«è¨˜ã•ã‚ŒãŸäºŒå一箇æ¡ã‚’ランダムã«ã¤ã¶ã‚„ãbotã§ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 4,
+ "friends_count": 5,
+ "listed_count": 1,
+ "created_at": "Sat Apr 06 01:19:55 +0000 2013",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 9639,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/3482551671/d9e749f7658b523bdd50b7584ed4ba6a_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/3482551671/d9e749f7658b523bdd50b7584ed4ba6a_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1330420010/1365212335",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:10 +0000 2014",
+ "id": 505874903094939650,
+ "id_str": "505874903094939648",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/mote_danshi1\" rel=\"nofollow\">モテモテ大作戦★男å­ç·¨</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2714526565,
+ "id_str": "2714526565",
+ "name": "モテモテ大作戦★男å­ç·¨",
+ "screen_name": "mote_danshi1",
+ "location": "",
+ "description": "ã‚„ã£ã±ã‚Šãƒ¢ãƒ†ãƒ¢ãƒ†ç”·å­ã«ãªã‚ŠãŸã„ï¼è‡ªåˆ†ã‚’磨ãヒントをã¿ã¤ã‘ãŸã„ï¼å¿œæ´ã—ã¦ãれる人㯠RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 664,
+ "friends_count": 1835,
+ "listed_count": 0,
+ "created_at": "Thu Aug 07 12:59:59 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 597,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/497368689386086400/7hqdKMzG_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/497368689386086400/7hqdKMzG_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2714526565/1407416898",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:10 +0000 2014",
+ "id": 505874902390276100,
+ "id_str": "505874902390276096",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/kokoro_meigen11\" rel=\"nofollow\">心ã«éŸ¿ãアツã„å言集</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2699261263,
+ "id_str": "2699261263",
+ "name": "心ã«éŸ¿ãアツã„å言集",
+ "screen_name": "kokoro_meigen11",
+ "location": "",
+ "description": "人生ã®æ ¼è¨€ã¯ã€äººã®å¿ƒã‚„人生を瞬時ã«ã«å‹•ã‹ã—ã¦ã—ã¾ã†ã“ã¨ãŒã‚る。\r\nãã‚“ãªè¨€è‘‰ã®é‡ã¿ã‚’味ã‚ãŠã†ã€‚\r\né¢ç™½ã‹ã£ãŸã‚‰RT & 相互フォローã§ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 183,
+ "friends_count": 1126,
+ "listed_count": 0,
+ "created_at": "Fri Aug 01 22:00:00 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 749,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/495328654126112768/1rKnNuWK_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/495328654126112768/1rKnNuWK_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2699261263/1406930543",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:10 +0000 2014",
+ "id": 505874902247677950,
+ "id_str": "505874902247677954",
+ "text": "RT @POTENZA_SUPERGT: ã‚ã‚ŠãŒã¨ã†ã”ã–ã„ã¾ã™ï¼â€œ@8CBR8: @POTENZA_SUPERGT 13時åŠã”ã‚一雨ããã†ã§ã™ãŒã€ç„¡äº‹å…¨è»Šæ±ºå‹ãƒ¬ãƒ¼ã‚¹å®Œèµ°å‡ºæ¥ã‚‹ã“ã¨ç¥ˆã£ã¦ã¾ã™ï¼ http://t.co/FzTyFnt9xHâ€",
+ "source": "<a href=\"http://jigtwi.jp/?p=1\" rel=\"nofollow\">jigtwi</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1021030416,
+ "id_str": "1021030416",
+ "name": "narur",
+ "screen_name": "narur2",
+ "location": "æ™´ã‚Œã®å›½ãªã®ã«ä½•æ•…ã‹é–‹å¹•æˆ¦ã§ã¯é›¨ã‚„雪や冰や霰ãŒé™ã‚‹âœ¨",
+ "description": "F1.GP2.Superformula.SuperGT.F3...\nスーパーGTãŒå¤§å¥½ã♡車ãŒå¥½ãï¼æ–°å¹¹ç·šã‚‚好ãï¼é£›è¡Œæ©Ÿã‚‚好ãï¼ã“ã£ãり別アカã§ã™(๑´ㅂ`๑)♡*.+ã‚œ",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 257,
+ "friends_count": 237,
+ "listed_count": 2,
+ "created_at": "Wed Dec 19 01:14:41 +0000 2012",
+ "favourites_count": 547,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 55417,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/462180217574789121/1Jf6m_2L.jpeg",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/462180217574789121/1Jf6m_2L.jpeg",
+ "profile_background_tile": true,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/444312241395863552/FKl40ebQ_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/444312241395863552/FKl40ebQ_normal.jpeg",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:05:11 +0000 2014",
+ "id": 505868866686169100,
+ "id_str": "505868866686169089",
+ "text": "ã‚ã‚ŠãŒã¨ã†ã”ã–ã„ã¾ã™ï¼â€œ@8CBR8: @POTENZA_SUPERGT 13時åŠã”ã‚一雨ããã†ã§ã™ãŒã€ç„¡äº‹å…¨è»Šæ±ºå‹ãƒ¬ãƒ¼ã‚¹å®Œèµ°å‡ºæ¥ã‚‹ã“ã¨ç¥ˆã£ã¦ã¾ã™ï¼ http://t.co/FzTyFnt9xHâ€",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": 505868690588303360,
+ "in_reply_to_status_id_str": "505868690588303360",
+ "in_reply_to_user_id": 333344408,
+ "in_reply_to_user_id_str": "333344408",
+ "in_reply_to_screen_name": "8CBR8",
+ "user": {
+ "id": 359324738,
+ "id_str": "359324738",
+ "name": "POTENZA_SUPERGT",
+ "screen_name": "POTENZA_SUPERGT",
+ "location": "",
+ "description": "ブリヂストンã®ã‚¹ãƒãƒ¼ãƒ„タイヤ「POTENZAã€ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã™ã€‚レースやタイヤã®äº‹ãªã©ã‚’ã¤ã¶ã‚„ãã¾ã™ã€‚今シーズンも「ãƒãƒ£ãƒ³ãƒ”オンタイヤã®ç§°å·ã¯è­²ã‚‰ãªã„ã€ã‚’キャッãƒã‚³ãƒ”ーã«ã€ã‚¿ã‚¤ãƒ¤ä¾›çµ¦ãƒãƒ¼ãƒ ã‚’全力ã§ã‚µãƒãƒ¼ãƒˆã—ã¦ã„ãã¾ã™ã®ã§ã€å¿œæ´ã‚ˆã‚ã—ããŠé¡˜ã„ã—ã¾ã™ï¼ãªãŠã€è¿”ä¿¡ãŒã§ããªã„å ´åˆã‚‚ã‚ã‚Šã¾ã™ã®ã§ã€ã”了承よã‚ã—ããŠé¡˜ã„致ã—ã¾ã™ã€‚",
+ "url": "http://t.co/LruVPk5x4K",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/LruVPk5x4K",
+ "expanded_url": "http://www.bridgestone.co.jp/sc/potenza/",
+ "display_url": "bridgestone.co.jp/sc/potenza/",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 9612,
+ "friends_count": 308,
+ "listed_count": 373,
+ "created_at": "Sun Aug 21 11:33:38 +0000 2011",
+ "favourites_count": 26,
+ "utc_offset": -36000,
+ "time_zone": "Hawaii",
+ "geo_enabled": true,
+ "verified": false,
+ "statuses_count": 10032,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "131516",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif",
+ "profile_background_tile": true,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/1507885396/TW_image_normal.jpg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/1507885396/TW_image_normal.jpg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/359324738/1402546267",
+ "profile_link_color": "FF2424",
+ "profile_sidebar_border_color": "EEEEEE",
+ "profile_sidebar_fill_color": "EFEFEF",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 7,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "8CBR8",
+ "name": "CBR Rider #17 KEIHIN",
+ "id": 333344408,
+ "id_str": "333344408",
+ "indices": [
+ 12,
+ 18
+ ]
+ },
+ {
+ "screen_name": "POTENZA_SUPERGT",
+ "name": "POTENZA_SUPERGT",
+ "id": 359324738,
+ "id_str": "359324738",
+ "indices": [
+ 20,
+ 36
+ ]
+ }
+ ],
+ "media": [
+ {
+ "id": 505868690252779500,
+ "id_str": "505868690252779521",
+ "indices": [
+ 75,
+ 97
+ ],
+ "media_url": "http://pbs.twimg.com/media/BwU05MGCUAEY6Wu.jpg",
+ "media_url_https": "https://pbs.twimg.com/media/BwU05MGCUAEY6Wu.jpg",
+ "url": "http://t.co/FzTyFnt9xH",
+ "display_url": "pic.twitter.com/FzTyFnt9xH",
+ "expanded_url": "http://twitter.com/8CBR8/status/505868690588303360/photo/1",
+ "type": "photo",
+ "sizes": {
+ "medium": {
+ "w": 600,
+ "h": 399,
+ "resize": "fit"
+ },
+ "thumb": {
+ "w": 150,
+ "h": 150,
+ "resize": "crop"
+ },
+ "large": {
+ "w": 1024,
+ "h": 682,
+ "resize": "fit"
+ },
+ "small": {
+ "w": 340,
+ "h": 226,
+ "resize": "fit"
+ }
+ },
+ "source_status_id": 505868690588303360,
+ "source_status_id_str": "505868690588303360"
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ "retweet_count": 7,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "POTENZA_SUPERGT",
+ "name": "POTENZA_SUPERGT",
+ "id": 359324738,
+ "id_str": "359324738",
+ "indices": [
+ 3,
+ 19
+ ]
+ },
+ {
+ "screen_name": "8CBR8",
+ "name": "CBR Rider #17 KEIHIN",
+ "id": 333344408,
+ "id_str": "333344408",
+ "indices": [
+ 33,
+ 39
+ ]
+ },
+ {
+ "screen_name": "POTENZA_SUPERGT",
+ "name": "POTENZA_SUPERGT",
+ "id": 359324738,
+ "id_str": "359324738",
+ "indices": [
+ 41,
+ 57
+ ]
+ }
+ ],
+ "media": [
+ {
+ "id": 505868690252779500,
+ "id_str": "505868690252779521",
+ "indices": [
+ 96,
+ 118
+ ],
+ "media_url": "http://pbs.twimg.com/media/BwU05MGCUAEY6Wu.jpg",
+ "media_url_https": "https://pbs.twimg.com/media/BwU05MGCUAEY6Wu.jpg",
+ "url": "http://t.co/FzTyFnt9xH",
+ "display_url": "pic.twitter.com/FzTyFnt9xH",
+ "expanded_url": "http://twitter.com/8CBR8/status/505868690588303360/photo/1",
+ "type": "photo",
+ "sizes": {
+ "medium": {
+ "w": 600,
+ "h": 399,
+ "resize": "fit"
+ },
+ "thumb": {
+ "w": 150,
+ "h": 150,
+ "resize": "crop"
+ },
+ "large": {
+ "w": 1024,
+ "h": 682,
+ "resize": "fit"
+ },
+ "small": {
+ "w": 340,
+ "h": 226,
+ "resize": "fit"
+ }
+ },
+ "source_status_id": 505868690588303360,
+ "source_status_id_str": "505868690588303360"
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:09 +0000 2014",
+ "id": 505874901689851900,
+ "id_str": "505874901689851904",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/danshi_honne1\" rel=\"nofollow\">ã“ã“ã ã‘ã®æœ¬éŸ³â˜…ç”·å­ç·¨</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2762136439,
+ "id_str": "2762136439",
+ "name": "ã“ã“ã ã‘ã®æœ¬éŸ³â˜…ç”·å­ç·¨",
+ "screen_name": "danshi_honne1",
+ "location": "",
+ "description": "æ€ã£ã¦ã‚‹ã‘ã©è¨€ãˆãªã„ï¼ã§ã‚‚ホントã¯è¨€ã„ãŸã„ã“ã¨ã€å®Ÿã¯ã„ã£ã±ã„ã‚ã‚‹ã‚“ã§ã™ï¼ \r\nãã‚“ãªç”·å­ã®æœ¬éŸ³ã‚’ã€ã¤ã¶ã‚„ãã¾ã™ã€‚ \r\nãã®æ°—æŒã‚ã‹ã‚‹ã£ã¦äººã¯ RT & フォローãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 101,
+ "friends_count": 985,
+ "listed_count": 0,
+ "created_at": "Sun Aug 24 11:11:30 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 209,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503500282840354816/CEv8UMay_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503500282840354816/CEv8UMay_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2762136439/1408878822",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:09 +0000 2014",
+ "id": 505874900939046900,
+ "id_str": "505874900939046912",
+ "text": "RT @UARROW_Y: よã†ã‹ã„体æ“第一を踊る国見英 http://t.co/SXoYWH98as",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2454426158,
+ "id_str": "2454426158",
+ "name": "ã´ã‹ã‚Šã‚“",
+ "screen_name": "gncnToktTtksg",
+ "location": "",
+ "description": "銀魂/é»’ãƒã‚¹/進撃/ãƒã‚¤ã‚­ãƒ¥ãƒ¼/BLEACH/ã†ãŸãƒ—リ/鈴木é”央ã•ã‚“/神谷浩å²ã•ã‚“ 気軽ã«ãƒ•ã‚©ãƒ­ãƒ¼ã—ã¦ãã ã•ã„(^∇^)✨",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 1274,
+ "friends_count": 1320,
+ "listed_count": 17,
+ "created_at": "Sun Apr 20 07:48:53 +0000 2014",
+ "favourites_count": 2314,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 5868,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/457788684146716672/KCOy0S75_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/457788684146716672/KCOy0S75_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2454426158/1409371302",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:45 +0000 2014",
+ "id": 505871779949051900,
+ "id_str": "505871779949051904",
+ "text": "よã†ã‹ã„体æ“第一を踊る国見英 http://t.co/SXoYWH98as",
+ "source": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1261662588,
+ "id_str": "1261662588",
+ "name": "ゆã†çŸ¢",
+ "screen_name": "UARROW_Y",
+ "location": "ã¤ãり出ãã†å›½å½±ã®æ³¢ 広ã’よã†å›½å½±ã®è¼ª",
+ "description": "HQ!! æˆäººæ¸ˆè…女å­ã€‚日常ツイート多ã„ã§ã™ã€‚赤葦京治夢豚クソツイå«ã¿ã¾ã™æ³¨æ„。フォローをãŠè€ƒãˆã®éš›ã¯ãƒ—ロフã”一読ãŠé¡˜ã„致ã—ã¾ã™ã€‚FRBãŠæ°—軽ã«",
+ "url": "http://t.co/LFX2XOzb0l",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/LFX2XOzb0l",
+ "expanded_url": "http://twpf.jp/UARROW_Y",
+ "display_url": "twpf.jp/UARROW_Y",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 265,
+ "friends_count": 124,
+ "listed_count": 12,
+ "created_at": "Tue Mar 12 10:42:17 +0000 2013",
+ "favourites_count": 6762,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": true,
+ "verified": false,
+ "statuses_count": 55946,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/502095104618663937/IzuPYx3E_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/502095104618663937/IzuPYx3E_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1261662588/1408618604",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 29,
+ "favorite_count": 54,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/SXoYWH98as",
+ "expanded_url": "http://twitter.com/UARROW_Y/status/505871779949051904/photo/1",
+ "display_url": "pic.twitter.com/SXoYWH98as",
+ "indices": [
+ 15,
+ 37
+ ]
+ }
+ ],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ "retweet_count": 29,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/SXoYWH98as",
+ "expanded_url": "http://twitter.com/UARROW_Y/status/505871779949051904/photo/1",
+ "display_url": "pic.twitter.com/SXoYWH98as",
+ "indices": [
+ 29,
+ 51
+ ]
+ }
+ ],
+ "user_mentions": [
+ {
+ "screen_name": "UARROW_Y",
+ "name": "ゆã†çŸ¢",
+ "id": 1261662588,
+ "id_str": "1261662588",
+ "indices": [
+ 3,
+ 12
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:09 +0000 2014",
+ "id": 505874900561580000,
+ "id_str": "505874900561580032",
+ "text": "今日ã¯ä¸€é«˜ã¨ä¸‰æ¡œï¼ˆãƒ»Î¸ãƒ»ï¼‰\n光梨ã¡ã‚ƒã‚“ã«ä¼šãˆãªã„ã‹ãªã€œ",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1366375976,
+ "id_str": "1366375976",
+ "name": "ゆã„ã®",
+ "screen_name": "yuino1006",
+ "location": "",
+ "description": "ã•ã‚“ãŠã† ç”·ãƒã‚¹ãƒžãƒ2ã­ã‚“(^ω^)",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 270,
+ "friends_count": 260,
+ "listed_count": 0,
+ "created_at": "Sat Apr 20 07:02:08 +0000 2013",
+ "favourites_count": 1384,
+ "utc_offset": 32400,
+ "time_zone": "Irkutsk",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 5202,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/505354401448349696/nxVFEQQ4_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/505354401448349696/nxVFEQQ4_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1366375976/1399989379",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:09 +0000 2014",
+ "id": 505874899324248060,
+ "id_str": "505874899324248064",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/kyoukan_aru\" rel=\"nofollow\">共感★絶対ã‚ã‚‹ã‚ã‚‹ww</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2704420069,
+ "id_str": "2704420069",
+ "name": "共感★絶対ã‚ã‚‹ã‚ã‚‹ww",
+ "screen_name": "kyoukan_aru",
+ "location": "",
+ "description": "ã¿ã‚“ãªã«ã‚‚ã‚ã‹ã£ã¦ã‚‚らãˆã‚‹ã€ã‚ã‚‹ã‚るを見ã¤ã‘ãŸã„。\r\né¢ç™½ã‹ã£ãŸã‚‰RT & 相互フォローã§ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 857,
+ "friends_count": 1873,
+ "listed_count": 0,
+ "created_at": "Sun Aug 03 15:50:40 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 682,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/495960812670836737/1LqkoyvU_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/495960812670836737/1LqkoyvU_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2704420069/1407081298",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:09 +0000 2014",
+ "id": 505874898493796350,
+ "id_str": "505874898493796352",
+ "text": "RT @assam_house: 泉田新潟県知事ã¯ã€æ±é›»ã®ç”³è«‹æ›¸æ出を容èªã•ã›ã‚‰ã‚ŒãŸã ã‘ã§ã€å†ç¨¼åƒã«å¿…è¦ãªã€ŒåŒæ„ã€ã¯ã¾ã ä¸Žãˆã¦ã„ã¾ã›ã‚“。今ã¾ã§æŸå´Žåˆˆç¾½ã®å†ç¨¼åƒã‚’抑ãˆç¶šã‘ã¦ããŸçŸ¥äº‹ã«ã€ã‚‚ã†ä¸€è¸ã‚“張りをãŠé¡˜ã„ã™ã‚‹æ„見をé€ã£ã¦ä¸‹ã•ã„。全国ã®çš†æ§˜ã€ãŠé¡˜ã„ã—ã¾ã™ï¼\nhttp://t.co…",
+ "source": "<a href=\"http://jigtwi.jp/?p=1001\" rel=\"nofollow\">jigtwi for Android</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 960765968,
+ "id_str": "960765968",
+ "name": "ã•ã¡",
+ "screen_name": "sachitaka_dears",
+ "location": "宮城県",
+ "description": "動物関連ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã™ã€‚サブアカウント@sachi_dears (ã•ã¡ â·) ã‚‚ã‚ã‚Šã¾ã™ã€‚『心ã‚ã‚‹ã‚‚ã®ã¯çš†ã€æ„›ã—æ„›ã•ã‚Œã‚‹ãŸã‚ã«ç”Ÿã¾ã‚Œã¦ããŸã€‚ãã—ã¦æ„›æƒ…ã‚’æ„Ÿã˜ãªãŒã‚‰ç”Ÿã‚’å…¨ã†ã™ã‚‹ã¹ããªã‚“ã ã€",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 3212,
+ "friends_count": 3528,
+ "listed_count": 91,
+ "created_at": "Tue Nov 20 16:30:53 +0000 2012",
+ "favourites_count": 3180,
+ "utc_offset": 32400,
+ "time_zone": "Irkutsk",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 146935,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/3659653229/5b698df67f5d105400e9077f5ea50e91_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/3659653229/5b698df67f5d105400e9077f5ea50e91_normal.png",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Tue Aug 19 11:00:53 +0000 2014",
+ "id": 501685228427964400,
+ "id_str": "501685228427964417",
+ "text": "泉田新潟県知事ã¯ã€æ±é›»ã®ç”³è«‹æ›¸æ出を容èªã•ã›ã‚‰ã‚ŒãŸã ã‘ã§ã€å†ç¨¼åƒã«å¿…è¦ãªã€ŒåŒæ„ã€ã¯ã¾ã ä¸Žãˆã¦ã„ã¾ã›ã‚“。今ã¾ã§æŸå´Žåˆˆç¾½ã®å†ç¨¼åƒã‚’抑ãˆç¶šã‘ã¦ããŸçŸ¥äº‹ã«ã€ã‚‚ã†ä¸€è¸ã‚“張りをãŠé¡˜ã„ã™ã‚‹æ„見をé€ã£ã¦ä¸‹ã•ã„。全国ã®çš†æ§˜ã€ãŠé¡˜ã„ã—ã¾ã™ï¼\nhttp://t.co/9oH5cgpy1q",
+ "source": "<a href=\"http://twittbot.net/\" rel=\"nofollow\">twittbot.net</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1104771276,
+ "id_str": "1104771276",
+ "name": "アッサム山中(殺処分ゼロã«ä¸€ç¥¨ï¼‰",
+ "screen_name": "assam_house",
+ "location": "新潟県æŸå´Žå¸‚",
+ "description": "アッサム山中ã®è¶£å‘³ç”¨ã‚¢ã‚«ã€‚当分ã®é–“ã€é¸æŒ™å•“発用ã¨ã—ã¦ã‚‚使ã£ã¦ã„ãã¾ã™ã€‚ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒã‚¢ãƒƒã‚µãƒ å±±ä¸­æœ¬äººã®ã‚‚ã®ã§ã‚る事㯠@assam_yamanaka ã®ãƒ—ロフã§ã”確èªä¸‹ã•ã„。\r\nå…¬é¸æ³•ã«ä¿‚る表示\r\n庶民新党 #脱原発 http://t.co/96UqoCo0oU\r\nonestep.revival@gmail.com",
+ "url": "http://t.co/AEOCATaNZc",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/AEOCATaNZc",
+ "expanded_url": "http://www.assam-house.net/",
+ "display_url": "assam-house.net",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": [
+ {
+ "url": "http://t.co/96UqoCo0oU",
+ "expanded_url": "http://blog.assam-house.net/datsu-genpatsu/index.html",
+ "display_url": "blog.assam-house.net/datsu-genpatsu…",
+ "indices": [
+ 110,
+ 132
+ ]
+ }
+ ]
+ }
+ },
+ "protected": false,
+ "followers_count": 2977,
+ "friends_count": 3127,
+ "listed_count": 64,
+ "created_at": "Sat Jan 19 22:10:13 +0000 2013",
+ "favourites_count": 343,
+ "utc_offset": 32400,
+ "time_zone": "Irkutsk",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 18021,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/378800000067217575/e0a85b440429ff50430a41200327dcb8_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000067217575/e0a85b440429ff50430a41200327dcb8_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1104771276/1408948288",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 2,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/9oH5cgpy1q",
+ "expanded_url": "http://www.pref.niigata.lg.jp/kouhou/info.html",
+ "display_url": "pref.niigata.lg.jp/kouhou/info.ht…",
+ "indices": [
+ 111,
+ 133
+ ]
+ }
+ ],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ "retweet_count": 2,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/9oH5cgpy1q",
+ "expanded_url": "http://www.pref.niigata.lg.jp/kouhou/info.html",
+ "display_url": "pref.niigata.lg.jp/kouhou/info.ht…",
+ "indices": [
+ 139,
+ 140
+ ]
+ }
+ ],
+ "user_mentions": [
+ {
+ "screen_name": "assam_house",
+ "name": "アッサム山中(殺処分ゼロã«ä¸€ç¥¨ï¼‰",
+ "id": 1104771276,
+ "id_str": "1104771276",
+ "indices": [
+ 3,
+ 15
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:09 +0000 2014",
+ "id": 505874898468630500,
+ "id_str": "505874898468630528",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/osyare_pea\" rel=\"nofollow\">ãŠã—ゃれ★ペアルック</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2708607692,
+ "id_str": "2708607692",
+ "name": "ãŠã—ゃれ★ペアルック",
+ "screen_name": "osyare_pea",
+ "location": "",
+ "description": "ラブラブ度ãŒã‚¢ãƒƒãƒ—ã™ã‚‹ã€ç´ æ•µãªãƒšã‚¢ãƒ«ãƒƒã‚¯ã‚’見ã¤ã‘ã¦ç´¹ä»‹ã—ã¾ã™â™ª æ°—ã«å…¥ã£ãŸã‚‰ RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 129,
+ "friends_count": 1934,
+ "listed_count": 0,
+ "created_at": "Tue Aug 05 07:09:31 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 641,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/496554257676382208/Zgg0bmNu_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/496554257676382208/Zgg0bmNu_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2708607692/1407222776",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:08 +0000 2014",
+ "id": 505874897633951740,
+ "id_str": "505874897633951745",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/love_live55\" rel=\"nofollow\">LOVE ♥ ラブライブ</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745389137,
+ "id_str": "2745389137",
+ "name": "LOVE ♥ ラブライブ",
+ "screen_name": "love_live55",
+ "location": "",
+ "description": "ã¨ã«ã‹ã「ラブライブãŒå¥½ãã§ï½žã™â™¥ã€ \r\nラブライブファンã«ã¯ã€ãŸã¾ã‚‰ãªã„内容ã°ã‹ã‚Šé›†ã‚ã¦ã„ã¾ã™â™ª \r\næ°—ã«å…¥ã£ãŸã‚‰ RT & 相互フォローãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 251,
+ "friends_count": 969,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 15:45:40 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 348,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501757482448850944/x2uPpqRx_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501757482448850944/x2uPpqRx_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745389137/1408463342",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:08 +0000 2014",
+ "id": 505874896795086850,
+ "id_str": "505874896795086848",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/koisurudoress\" rel=\"nofollow\">æ‹ã™ã‚‹â™¡ãƒ‰ãƒ¬ã‚¹ã‚·ãƒªãƒ¼ã‚º</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2726346560,
+ "id_str": "2726346560",
+ "name": "æ‹ã™ã‚‹â™¡ãƒ‰ãƒ¬ã‚¹ã‚·ãƒªãƒ¼ã‚º",
+ "screen_name": "koisurudoress",
+ "location": "",
+ "description": "ã©ã‚Œã‚‚ã“れもã€è¦‹ã¦ã„ã‚‹ã ã‘ã§æ¬²ã—ããªã£ã¡ã‚ƒã†â™ª \r\n特別ãªæ—¥ã«ç€ã‚‹ç´ æ•µãªãƒ‰ãƒ¬ã‚¹ã‚’見ã¤ã‘ãŸã„ã§ã™ã€‚ \r\nç€ã¦ã¿ãŸã„ã¨æ€ã£ãŸã‚‰ RT & フォローãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 314,
+ "friends_count": 1900,
+ "listed_count": 0,
+ "created_at": "Tue Aug 12 14:10:35 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 471,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/499199619465621504/fg7sVusT_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/499199619465621504/fg7sVusT_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2726346560/1407853688",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:08 +0000 2014",
+ "id": 505874895964626940,
+ "id_str": "505874895964626944",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/doubutuzukan\" rel=\"nofollow\">胸キュン♥動物図鑑</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2759192574,
+ "id_str": "2759192574",
+ "name": "胸キュン♥動物図鑑",
+ "screen_name": "doubutuzukan",
+ "location": "",
+ "description": "ãµã¨ã—ãŸè¡¨æƒ…ã«æ€ã‚ãšã‚­ãƒ¥ãƒ³ã¨ã—ã¦ã—ã¾ã†â™ª \r\nãã‚“ãªæ„›ã—ã®å‹•ç‰©ãŸã¡ã®å†™çœŸã‚’見ã¤ã‘ã¾ã™ã€‚ \r\næ°—ã«å…¥ã£ãŸã‚‰ RT & フォローをã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 80,
+ "friends_count": 959,
+ "listed_count": 1,
+ "created_at": "Sat Aug 23 15:47:36 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 219,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503211559552688128/Ej_bixna_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503211559552688128/Ej_bixna_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2759192574/1408809101",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:08 +0000 2014",
+ "id": 505874895079608300,
+ "id_str": "505874895079608320",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/disney_para\" rel=\"nofollow\">ディズニー★パラダイス</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2719228561,
+ "id_str": "2719228561",
+ "name": "ディズニー★パラダイス",
+ "screen_name": "disney_para",
+ "location": "",
+ "description": "ディズニーã®ã‹ã‚ã„ã„ç”»åƒã€ãƒ‹ãƒ¥ãƒ¼ã‚¹æƒ…å ±ã€ã‚ã‚‹ã‚ã‚‹ãªã©ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª\r\nディズニーファン㯠RT & フォローもãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 331,
+ "friends_count": 1867,
+ "listed_count": 0,
+ "created_at": "Sat Aug 09 12:01:32 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 540,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/498076922488696832/Ti2AEuOT_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/498076922488696832/Ti2AEuOT_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2719228561/1407585841",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:08 +0000 2014",
+ "id": 505874894135898100,
+ "id_str": "505874894135898112",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/nama_fuushi\" rel=\"nofollow\">生々ã—ã„風刺画</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2714772727,
+ "id_str": "2714772727",
+ "name": "生々ã—ã„風刺画",
+ "screen_name": "nama_fuushi",
+ "location": "",
+ "description": "æ·±ã„æ„味ãŒè¾¼ã‚られãŸã€Œç”Ÿã€…ã—ã„風刺画ã€ã‚’見ã¤ã‘ã¾ã™ã€‚\r\n考ãˆã•ã›ã‚‰ã‚ŒãŸã‚‰ RT & 相互フォローã§ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 298,
+ "friends_count": 1902,
+ "listed_count": 1,
+ "created_at": "Thu Aug 07 15:04:45 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 595,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/497398363352875011/tS-5FPJB_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/497398363352875011/tS-5FPJB_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2714772727/1407424091",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:07 +0000 2014",
+ "id": 505874893347377150,
+ "id_str": "505874893347377152",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/arashi_suki1\" rel=\"nofollow\">åµâ˜…大好ãã£å¨˜</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2721682579,
+ "id_str": "2721682579",
+ "name": "åµâ˜…大好ãã£å¨˜",
+ "screen_name": "arashi_suki1",
+ "location": "",
+ "description": "ãªã‚“ã ã‹ã‚“ã è¨€ã£ã¦ã€ã‚„ã£ã±ã‚ŠåµãŒå¥½ããªã‚“ã§ã™â™ª\r\nã„ã‚ã„ã‚集ã‚ãŸã„ã®ã§ã€åµå¥½ããªäººã«è¦‹ã¦ã»ã—ã„ã§ã™ã€‚\r\næ°—ã«å…¥ã£ãŸã‚‰ RT & 相互フォローãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 794,
+ "friends_count": 1913,
+ "listed_count": 2,
+ "created_at": "Sun Aug 10 13:43:56 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 504,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/498465364733198336/RO6wupdc_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/498465364733198336/RO6wupdc_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2721682579/1407678436",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:07 +0000 2014",
+ "id": 505874893154426900,
+ "id_str": "505874893154426881",
+ "text": "RT @Takashi_Shiina: テレビã§ã€Œæˆäººç”·æ€§ã®ã‚«ãƒ­ãƒªãƒ¼æ‘‚å–é‡ã¯1900kcalã€ã¨ã‹è¨€ã£ã¦ã¦ã€ãã‚Œã¯ã„ã¾ã¾ã•ã«ç§ãŒãƒ€ã‚¤ã‚¨ãƒƒãƒˆã®ãŸã‚ã«å¿…æ­»ã§ã‚­ãƒ¼ãƒ—ã—よã†ã¨ã—ã¦ã„ã‚‹é‡ã§ã€ã€Œãã‚ŒãŒæ™®é€šãªã‚‰äººã¯ã„ã¤å¤©ä¸€ã‚„ココイãƒã«è¡Œã£ã¦å¤§ç››ã‚Šã‚’食ãˆã°ã„ã„ã‚“ã ï¼ã€ã¨æ€ã£ãŸã€‚",
+ "source": "<a href=\"http://twicca.r246.jp/\" rel=\"nofollow\">twicca</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 353516742,
+ "id_str": "353516742",
+ "name": "ãŠã—ã‚“ã“ー@土曜西ãˆ41a",
+ "screen_name": "oshin_koko",
+ "location": "ã“ãŸã¤",
+ "description": "ROMã£ã¦æ¥½ã—ã‚“ã§ã„る部分もã‚り無言フォロー多ã‚ã§ã™ã™ã¿ã¾ã›ã‚“…。ツイート数多ã‚・ã‚らã¶ã‚Šå¤šã‚ãªã®ã§ãƒ•ã‚©ãƒ­ãƒ¼éžæŽ¨å¥¨ã§ã™ã€‚最近ã¯æ—©å…µãƒ»å…µéƒ¨å—ã‘中心ã§ã™ãŒBLNLãªã‚“ã§ã‚‚好ãã§ã™ã€‚地雷少ãªã„ãŸã‚雑多ã«å‘Ÿãã¾ã™ã€‚è…・R18・ãƒã‚¿ãƒãƒ¬æœ‰ã‚‹ã®ã§ã”注æ„。他好ããªã‚¸ãƒ£ãƒ³ãƒ«ã¯ãƒ—ロフå‚照願ã„ã¾ã™ã€‚ 主催→@chounou_antholo",
+ "url": "http://t.co/mM1dG54NiO",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/mM1dG54NiO",
+ "expanded_url": "http://twpf.jp/oshin_koko",
+ "display_url": "twpf.jp/oshin_koko",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 479,
+ "friends_count": 510,
+ "listed_count": 43,
+ "created_at": "Fri Aug 12 05:53:13 +0000 2011",
+ "favourites_count": 3059,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 104086,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "000000",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/799871497/01583a031f83a45eba881c8acde729ee.jpeg",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/799871497/01583a031f83a45eba881c8acde729ee.jpeg",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/484347196523835393/iHaYxm-2_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/484347196523835393/iHaYxm-2_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/353516742/1369039651",
+ "profile_link_color": "FF96B0",
+ "profile_sidebar_border_color": "FFFFFF",
+ "profile_sidebar_fill_color": "95E8EC",
+ "profile_text_color": "3C3940",
+ "profile_use_background_image": false,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sat Aug 30 09:58:30 +0000 2014",
+ "id": 505655792733650940,
+ "id_str": "505655792733650944",
+ "text": "テレビã§ã€Œæˆäººç”·æ€§ã®ã‚«ãƒ­ãƒªãƒ¼æ‘‚å–é‡ã¯1900kcalã€ã¨ã‹è¨€ã£ã¦ã¦ã€ãã‚Œã¯ã„ã¾ã¾ã•ã«ç§ãŒãƒ€ã‚¤ã‚¨ãƒƒãƒˆã®ãŸã‚ã«å¿…æ­»ã§ã‚­ãƒ¼ãƒ—ã—よã†ã¨ã—ã¦ã„ã‚‹é‡ã§ã€ã€Œãã‚ŒãŒæ™®é€šãªã‚‰äººã¯ã„ã¤å¤©ä¸€ã‚„ココイãƒã«è¡Œã£ã¦å¤§ç››ã‚Šã‚’食ãˆã°ã„ã„ã‚“ã ï¼ã€ã¨æ€ã£ãŸã€‚",
+ "source": "<a href=\"http://janetter.net/\" rel=\"nofollow\">Janetter</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 126573583,
+ "id_str": "126573583",
+ "name": "椎å高志",
+ "screen_name": "Takashi_Shiina",
+ "location": "BABEL(超能力支æ´ç ”究局)",
+ "description": "漫画家。週刊少年サンデーã§ã€Žçµ¶å¯¾å¯æ†ãƒãƒ«ãƒ‰ãƒ¬ãƒ³ã€é€£è¼‰ä¸­ã€‚TVアニメ『THE UNLIMITED 兵部京介ã€å…¬å¼ã‚µã‚¤ãƒˆï¼žhttp://t.co/jVqBoBEc",
+ "url": "http://t.co/K3Oi83wM3w",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/K3Oi83wM3w",
+ "expanded_url": "http://cnanews.asablo.jp/blog/",
+ "display_url": "cnanews.asablo.jp/blog/",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": [
+ {
+ "url": "http://t.co/jVqBoBEc",
+ "expanded_url": "http://unlimited-zc.jp/index.html",
+ "display_url": "unlimited-zc.jp/index.html",
+ "indices": [
+ 59,
+ 79
+ ]
+ }
+ ]
+ }
+ },
+ "protected": false,
+ "followers_count": 110756,
+ "friends_count": 61,
+ "listed_count": 8159,
+ "created_at": "Fri Mar 26 08:54:51 +0000 2010",
+ "favourites_count": 25,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 27364,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "EDECE9",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme3/bg.gif",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme3/bg.gif",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/504597210772688896/Uvt4jgf5_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/504597210772688896/Uvt4jgf5_normal.png",
+ "profile_link_color": "088253",
+ "profile_sidebar_border_color": "D3D2CF",
+ "profile_sidebar_fill_color": "E3E2DE",
+ "profile_text_color": "634047",
+ "profile_use_background_image": false,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 221,
+ "favorite_count": 109,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 221,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "Takashi_Shiina",
+ "name": "椎å高志",
+ "id": 126573583,
+ "id_str": "126573583",
+ "indices": [
+ 3,
+ 18
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:07 +0000 2014",
+ "id": 505874892567244800,
+ "id_str": "505874892567244801",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/shimo_hentai\" rel=\"nofollow\">下ãƒã‚¿ï¼†ç¬‘変態雑学</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2762581922,
+ "id_str": "2762581922",
+ "name": "下ãƒã‚¿ï¼†ç¬‘変態雑学",
+ "screen_name": "shimo_hentai",
+ "location": "",
+ "description": "普通ã®äººã«ã¯æ€ã„ã¤ã‹ãªã„ã€ã¡ã‚‡ã£ã¨å¤‰æ…‹ãƒãƒƒã‚¯ãª 笑ãˆã‚‹ä¸‹ãƒã‚¿é›‘学をãŠå±Šã‘ã—ã¾ã™ã€‚ \r\nãŠã‚‚ã—ã‚ã‹ã£ãŸã‚‰ RT & フォローãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 37,
+ "friends_count": 990,
+ "listed_count": 0,
+ "created_at": "Sun Aug 24 14:13:20 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 212,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503545991950114816/K9yQbh1Q_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503545991950114816/K9yQbh1Q_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2762581922/1408889893",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:07 +0000 2014",
+ "id": 505874891778703360,
+ "id_str": "505874891778703360",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/kantaneigo1\" rel=\"nofollow\">超簡å˜â˜…åˆå¿ƒè€…英語</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2744544025,
+ "id_str": "2744544025",
+ "name": "超簡å˜â˜…åˆå¿ƒè€…英語",
+ "screen_name": "kantaneigo1",
+ "location": "",
+ "description": "ã™ãã«ä½¿ãˆã‚‹ãƒ•ãƒ¬ãƒ¼ã‚ºã‚„ç°¡å˜ãªä¼šè©±ã‚’紹介ã—ã¾ã™ã€‚ \r\nå°‘ã—ã¥ã¤ç·´ç¿’ã—ã¦ã€ã©ã‚“ã©ã‚“使ã£ã¦ã¿ã‚ˆã†â˜† \r\n使ã£ã¦ã¿ãŸã„ã¨æ€ã£ãŸã‚‰ RT & フォローãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 147,
+ "friends_count": 970,
+ "listed_count": 1,
+ "created_at": "Tue Aug 19 10:11:48 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 345,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501676136321929216/4MLpyHe3_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501676136321929216/4MLpyHe3_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744544025/1408443928",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:07 +0000 2014",
+ "id": 505874891032121340,
+ "id_str": "505874891032121344",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/ima_handsign\" rel=\"nofollow\">ç¾ä»£ã®ãƒãƒ³ãƒ‰ã‚µã‚¤ãƒ³</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2762816814,
+ "id_str": "2762816814",
+ "name": "ç¾ä»£ã®ãƒãƒ³ãƒ‰ã‚µã‚¤ãƒ³",
+ "screen_name": "ima_handsign",
+ "location": "",
+ "description": "イザã¨ã„ã†æ™‚ã‚„ã€å›°ã£ãŸæ™‚ã«ã€å¿…ãšå½¹ã«ç«‹ã¤ãƒãƒ³ãƒ‰ã‚µã‚¤ãƒ³ã®ã‚ªãƒ³ãƒ‘レードã§ã™â™ª \r\n使ã£ã¦ã¿ãŸããªã£ãŸã‚‰ RT & フォローãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 95,
+ "friends_count": 996,
+ "listed_count": 0,
+ "created_at": "Sun Aug 24 15:33:58 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 210,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503566188253687809/7wtdp1AC_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503566188253687809/7wtdp1AC_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2762816814/1408894540",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:07 +0000 2014",
+ "id": 505874890247782400,
+ "id_str": "505874890247782401",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/anata_iionna\" rel=\"nofollow\">今日ã‹ã‚‰ã‚¢ãƒŠã‚¿ã‚‚イイ女♪</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2714167411,
+ "id_str": "2714167411",
+ "name": "今日ã‹ã‚‰ã‚¢ãƒŠã‚¿ã‚‚イイ女♪",
+ "screen_name": "anata_iionna",
+ "location": "",
+ "description": "ã¿ã‚“ãªãŒçŸ¥ã‚ŠãŸã„ イイ女ã®ç§˜å¯†ã‚’見ã¤ã‘ã¾ã™â™ª ã„ã„ãªï½žã¨æ€ã£ã¦ãã‚ŒãŸäººã¯ RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 390,
+ "friends_count": 1425,
+ "listed_count": 0,
+ "created_at": "Thu Aug 07 09:27:59 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 609,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/497314455655436288/dz7P3-fy_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/497314455655436288/dz7P3-fy_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2714167411/1407404214",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:07 +0000 2014",
+ "id": 505874890218434560,
+ "id_str": "505874890218434560",
+ "text": "@kohecyan3 \nåå‰:上野滉平\n呼ã³æ–¹:ã†ãˆã®\n呼ã°ã‚Œæ–¹:ãšã‚‹ã‹ã‚\n第一å°è±¡:éŽå‰°ãªä¿ºã‚¤ã‚±ãƒ¡ãƒ³ã§ã™ã‚¢ãƒ”ール\n今ã®å°è±¡:ãƒãƒ¼ãƒãƒªãƒ¼ã®æ™‚計\n好ããªã¨ã“ã‚:ã‚ã®è‡ªä¿¡ã•ã€ç¬‘ã„ãŒçµ¶ãˆãªã„\n一言:大学å—ã‹ã£ãŸã®ï¼Ÿå¿œæ´ã—ã¦ã‚‹ã€œ(*^^*)ï¼\n\n#RTã—ãŸäººã«ã‚„ã‚‹\nã¡ã‚‡ã£ã¨ã‚„ã£ã¦ã¿ã‚‹ç¬‘",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": 2591363659,
+ "in_reply_to_user_id_str": "2591363659",
+ "in_reply_to_screen_name": "kohecyan3",
+ "user": {
+ "id": 2613282517,
+ "id_str": "2613282517",
+ "name": "K",
+ "screen_name": "kawazurukenna",
+ "location": "",
+ "description": "# I surprise even my self",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 113,
+ "friends_count": 185,
+ "listed_count": 0,
+ "created_at": "Wed Jul 09 09:39:13 +0000 2014",
+ "favourites_count": 157,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 242,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/502436858135973888/PcUU0lov_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/502436858135973888/PcUU0lov_normal.jpeg",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [
+ {
+ "text": "RTã—ãŸäººã«ã‚„ã‚‹",
+ "indices": [
+ 119,
+ 128
+ ]
+ }
+ ],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "kohecyan3",
+ "name": "上野滉平",
+ "id": 2591363659,
+ "id_str": "2591363659",
+ "indices": [
+ 0,
+ 10
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:07 +0000 2014",
+ "id": 505874889392156700,
+ "id_str": "505874889392156672",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/iq_tameshi\" rel=\"nofollow\">IQ★力ã ã‚ã—</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2709308887,
+ "id_str": "2709308887",
+ "name": "IQ★力ã ã‚ã—",
+ "screen_name": "iq_tameshi",
+ "location": "",
+ "description": "解ã‘ã‚‹ã¨æ¥½ã—ã„気分ã«ãªã‚Œã‚‹å•é¡Œã‚’見ã¤ã‘ã¦ç´¹ä»‹ã—ã¾ã™â™ªé¢ç™½ã‹ã£ãŸã‚‰ RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 443,
+ "friends_count": 1851,
+ "listed_count": 1,
+ "created_at": "Tue Aug 05 13:14:30 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 664,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/496646485266558977/W_W--qV__normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/496646485266558977/W_W--qV__normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2709308887/1407244754",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:06 +0000 2014",
+ "id": 505874888817532900,
+ "id_str": "505874888817532928",
+ "text": "第一三è»ã‹ã‚‰ï¼’個師団ãŒåŒ—ã¸ç§»å‹•ä¸­ã‚‰ã—ã„     ã“ã®èª¿å­ã§ã¯æº€å·žã«é™¸è»å…µåŠ›ãŒã‚ãµã‚Œã‹ãˆã‚‹",
+ "source": "<a href=\"http://m.blogs.yahoo.co.jp/misa_1273\" rel=\"nofollow\">如月克己</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1171299612,
+ "id_str": "1171299612",
+ "name": "如月 克己",
+ "screen_name": "kisaragi_katumi",
+ "location": "満州",
+ "description": "Gパングã®Aåž‹K月克己中尉ã®éžå…¬å¼botã§ã™ã€‚ 主ã«ä¸ƒå·»ã¨å…«å·»ãŒä¸­å¿ƒã®å°è©žã‚’ã¤ã¶ã‚„ãã¾ã™ã€‚ 4/18.å°è©žè¿½åŠ ã—ã¾ã—ãŸ/ç¾åœ¨è©¦é‹è»¢ä¸­/ç¾åœ¨è»½ã„挨拶ã ã‘TLå応。/追加ã—ãŸã„å°è©žã‚„何ãŠã‹ã—ã„所ãŒã‚ã‚Šã¾ã—ãŸã‚‰DMやリプライã§/フォロー返ã—ã¯æ‰‹å‹•ã§ã™/",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 65,
+ "friends_count": 63,
+ "listed_count": 0,
+ "created_at": "Tue Feb 12 08:21:38 +0000 2013",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 27219,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/3242847112/0ce536444c94cbec607229022d43a27a_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/3242847112/0ce536444c94cbec607229022d43a27a_normal.jpeg",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:06 +0000 2014",
+ "id": 505874888616181760,
+ "id_str": "505874888616181760",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/tokuda_ouen1\" rel=\"nofollow\">徳田有希★応æ´éšŠ</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2766021865,
+ "id_str": "2766021865",
+ "name": "徳田有希★応æ´éšŠ",
+ "screen_name": "tokuda_ouen1",
+ "location": "",
+ "description": "女å­ä¸­é«˜ç”Ÿã«å¤§äººæ°—ww ã„ã‚„ã•ã‚Œã‚‹ã‚¤ãƒ©ã‚¹ãƒˆã‚’紹介ã—ã¾ã™ã€‚ \r\nã¿ã‚“ãªã§ RTã—ã¦å¿œæ´ã—よã†ï½žâ™ª \r\n「éžå…¬å¼ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã™ã€",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 123,
+ "friends_count": 978,
+ "listed_count": 0,
+ "created_at": "Mon Aug 25 10:48:41 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 210,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503857235802333184/YS0sDN6q_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503857235802333184/YS0sDN6q_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2766021865/1408963998",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:06 +0000 2014",
+ "id": 505874887802511360,
+ "id_str": "505874887802511361",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/fujyoshinoheya\" rel=\"nofollow\">è…女å­ã®â˜†éƒ¨å±‹</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2744683982,
+ "id_str": "2744683982",
+ "name": "è…女å­ã®â˜†éƒ¨å±‹",
+ "screen_name": "fujyoshinoheya",
+ "location": "",
+ "description": "è…女å­ã«ã—ã‹ã‚ã‹ã‚‰ãªã„ãƒã‚¿ã‚„ã€ã‚ã‚‹ã‚るを見ã¤ã‘ã¦ã„ãã¾ã™ã€‚ \r\nä»–ã«ã¯ã€BL~èŒãˆã‚­ãƒ¥ãƒ³ç³»ã¾ã§ã€è…ã®ãŸã‚ã®ç”»åƒã‚’集ã‚ã¦ã„ã¾ã™â™ª \r\nåŒã˜å¢ƒé‡ã®äººã«ã¯ã€ã‚ã‹ã£ã¦ã‚‚らãˆã‚‹ã¨æ€ã†ã®ã§ã€æ°—軽㫠RT & フォローãŠé¡˜ã„ã—ã¾ã™â˜†",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 241,
+ "friends_count": 990,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 11:47:21 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 345,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501697365590306817/GLP_QH_b_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501697365590306817/GLP_QH_b_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744683982/1408448984",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:06 +0000 2014",
+ "id": 505874887009767400,
+ "id_str": "505874887009767424",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/moe_rate\" rel=\"nofollow\">èŒãˆèŠ¸è¡“★ラテアート</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2763178045,
+ "id_str": "2763178045",
+ "name": "èŒãˆèŠ¸è¡“★ラテアート",
+ "screen_name": "moe_rate",
+ "location": "",
+ "description": "ã“ã“ã¾ã§æ¥ã‚‹ã¨ã€ã‚‚ã¯ã‚„芸術!! 見ã¦ã‚‹ã ã‘ã§æ¥½ã—ã„♪ \r\nãã‚“ãªãƒ©ãƒ†ã‚¢ãƒ¼ãƒˆã‚’ã€ã¨ã“ã¨ã‚“探ã—ã¾ã™ã€‚ \r\nスゴイã¨æ€ã£ãŸã‚‰ RT & フォローãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 187,
+ "friends_count": 998,
+ "listed_count": 0,
+ "created_at": "Sun Aug 24 16:53:16 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 210,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503586151764992000/RC80it20_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503586151764992000/RC80it20_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2763178045/1408899447",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:06 +0000 2014",
+ "id": 505874886225448960,
+ "id_str": "505874886225448960",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/zenbu_johnnys\" rel=\"nofollow\">全部★ジャニーズ図鑑</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2724158970,
+ "id_str": "2724158970",
+ "name": "全部★ジャニーズ図鑑",
+ "screen_name": "zenbu_johnnys",
+ "location": "",
+ "description": "ジャニーズã®ã‚«ãƒƒã‚³ã‚¤ã‚¤ç”»åƒã€ãŠã‚‚ã—ã‚エピソードãªã©ã‚’発信ã—ã¾ã™ã€‚\r\n「éžå…¬å¼ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã™ã€\r\nジャニーズ好ããªäººã¯ã€æ˜¯éž RT & フォローãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 738,
+ "friends_count": 1838,
+ "listed_count": 0,
+ "created_at": "Mon Aug 11 15:50:08 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 556,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/498859581057945600/ncMKwdvC_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/498859581057945600/ncMKwdvC_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2724158970/1407772462",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:06 +0000 2014",
+ "id": 505874885810200600,
+ "id_str": "505874885810200576",
+ "text": "RT @naopisu_: 呼ã³æ–¹:\n呼ã°ã‚Œæ–¹:\n第一å°è±¡:\n今ã®å°è±¡:\n好ããªã¨ã“ã‚:\n家æ—ã«ã™ã‚‹ãªã‚‰:\n最後ã«ä¸€è¨€:\n#RTã—ãŸäººã«ã‚„ã‚‹\n\nãŠè…¹ç—›ãã¦å¯ã‚Œãªã„ã‹ã‚‰ã‚„ã‚‹ww\nã ã‚Œã§ã‚‚ã©ã†ãžã€œðŸ˜ðŸ™Œ",
+ "source": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2347898072,
+ "id_str": "2347898072",
+ "name": "ã«ãŸã«ãŸ",
+ "screen_name": "syo6660129",
+ "location": "",
+ "description": "",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 64,
+ "friends_count": 70,
+ "listed_count": 1,
+ "created_at": "Mon Feb 17 04:29:46 +0000 2014",
+ "favourites_count": 58,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 145,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/485603672118669314/73uh_xRS_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/485603672118669314/73uh_xRS_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2347898072/1396957619",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sat Aug 30 14:19:31 +0000 2014",
+ "id": 505721480261300200,
+ "id_str": "505721480261300224",
+ "text": "呼ã³æ–¹:\n呼ã°ã‚Œæ–¹:\n第一å°è±¡:\n今ã®å°è±¡:\n好ããªã¨ã“ã‚:\n家æ—ã«ã™ã‚‹ãªã‚‰:\n最後ã«ä¸€è¨€:\n#RTã—ãŸäººã«ã‚„ã‚‹\n\nãŠè…¹ç—›ãã¦å¯ã‚Œãªã„ã‹ã‚‰ã‚„ã‚‹ww\nã ã‚Œã§ã‚‚ã©ã†ãžã€œðŸ˜ðŸ™Œ",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 856045488,
+ "id_str": "856045488",
+ "name": "ãªãŠã´ã™",
+ "screen_name": "naopisu_",
+ "location": "Fujino 65th ⇢ Sagaso 12A(LJK",
+ "description": "ï¼¼ ã‚‚ã†ã™ã18æ­³ “Only Oneâ€ã«ãªã‚‹ ï¼",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 267,
+ "friends_count": 259,
+ "listed_count": 2,
+ "created_at": "Mon Oct 01 08:36:23 +0000 2012",
+ "favourites_count": 218,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 1790,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/496321592553525249/tuzX9ByR_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/496321592553525249/tuzX9ByR_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/856045488/1407118111",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 23,
+ "favorite_count": 1,
+ "entities": {
+ "hashtags": [
+ {
+ "text": "RTã—ãŸäººã«ã‚„ã‚‹",
+ "indices": [
+ 47,
+ 56
+ ]
+ }
+ ],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 23,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [
+ {
+ "text": "RTã—ãŸäººã«ã‚„ã‚‹",
+ "indices": [
+ 61,
+ 70
+ ]
+ }
+ ],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "naopisu_",
+ "name": "ãªãŠã´ã™",
+ "id": 856045488,
+ "id_str": "856045488",
+ "indices": [
+ 3,
+ 12
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:06 +0000 2014",
+ "id": 505874885474656260,
+ "id_str": "505874885474656256",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/line_aru1\" rel=\"nofollow\">爆笑★LINE ã‚ã‚‹ã‚ã‚‹</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2709561589,
+ "id_str": "2709561589",
+ "name": "爆笑★LINE ã‚ã‚‹ã‚ã‚‹",
+ "screen_name": "line_aru1",
+ "location": "",
+ "description": "æ€ã‚ãšç¬‘ã£ã¦ã—ã¾ã†LINEã§ã®ã‚„ã‚Šã¨ã‚Šã‚„ã€ã‚ã‚‹ã‚るを見ã¤ã‘ãŸã„ã§ã™â™ªé¢ç™½ã‹ã£ãŸã‚‰ RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 496,
+ "friends_count": 1875,
+ "listed_count": 1,
+ "created_at": "Tue Aug 05 15:01:30 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 687,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/496673793939492867/p1BN4YaW_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/496673793939492867/p1BN4YaW_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2709561589/1407251270",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:05 +0000 2014",
+ "id": 505874884627410940,
+ "id_str": "505874884627410944",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/misawahatugen\" rel=\"nofollow\">全力★ミサワ的w発言</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2734455415,
+ "id_str": "2734455415",
+ "name": "全力★ミサワ的w発言!!",
+ "screen_name": "misawahatugen",
+ "location": "",
+ "description": "ウザã™ãŽã¦ç¬‘ãˆã‚‹ãƒŸã‚µãƒ¯çš„å言やã€ãŠã‚‚ã—ã‚ミサワ画åƒã‚’集ã‚ã¦ã„ã¾ã™ã€‚ \r\nミサワを知らãªã„人ã§ã‚‚ã€ã„ããªã‚Šãƒ„ボã«ãƒãƒžã£ã¡ã‚ƒã†å†…容をãŠå±Šã‘ã—ã¾ã™ã€‚ \r\nウザã„ï½—ã¨æ€ã£ãŸã‚‰ RT & 相互フォローãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 144,
+ "friends_count": 1915,
+ "listed_count": 1,
+ "created_at": "Fri Aug 15 13:20:04 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 436,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/500271070834749444/HvengMe5_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/500271070834749444/HvengMe5_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2734455415/1408108944",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:05 +0000 2014",
+ "id": 505874883809521660,
+ "id_str": "505874883809521664",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/otakara_sotuaru\" rel=\"nofollow\">ãŠå®ww有å人å’アル特集</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2708183557,
+ "id_str": "2708183557",
+ "name": "ãŠå®ww有å人å’アル特集",
+ "screen_name": "otakara_sotuaru",
+ "location": "",
+ "description": "ã¿ã‚“ãªæ˜”ã¯è‹¥ã‹ã£ãŸã‚“ã§ã™ã­ã€‚今ã‹ã‚‰ã¯æƒ³åƒã‚‚ã¤ã‹ãªã„ã€ã‚ã®æœ‰å人を見ã¤ã‘ã¾ã™ã€‚\r\né¢ç™½ã‹ã£ãŸã‚‰ RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 286,
+ "friends_count": 1938,
+ "listed_count": 0,
+ "created_at": "Tue Aug 05 03:26:54 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 650,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/496499121276985344/hC8RoebP_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/496499121276985344/hC8RoebP_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2708183557/1407318758",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:05 +0000 2014",
+ "id": 505874883322970100,
+ "id_str": "505874883322970112",
+ "text": "レッドクリフã®ã‚­ãƒ£ãƒ©ã®ã“ã¨å¥³è£…ã£ã¦ããã‚ã‚ãŸwwwæœä¸€ã§é¢ç™½ã‹ã£ãŸ( ˘ω゜)笑",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1620730616,
+ "id_str": "1620730616",
+ "name": "ã²ãƒ¼ã¡ã‚ƒã‚“@橘芋å¥ã´",
+ "screen_name": "2nd_8hkr",
+ "location": "北ã®å¤§åœ°.95年組 ☞ 9/28.10/2(5).12/28",
+ "description": "THE SECOND/劇団EXILE/EXILE/二代目JSB ☞KENCHI.AKIRA.é’柳翔.å°æ£®éš¼.石井æ奈☜ Big Love ♡ Respect ..... ✠MATSU Origin✧ .㟠㡠㰠㪠'' ã„ ã‚‚ '' ã‘ ã‚“ ㄠ㡠゠ㆠ㕠んTEAM NACS 安田.戸次 Liebe !",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 109,
+ "friends_count": 148,
+ "listed_count": 0,
+ "created_at": "Thu Jul 25 16:09:29 +0000 2013",
+ "favourites_count": 783,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 9541,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/458760951060123648/Cocoxi-2_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/458760951060123648/Cocoxi-2_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1620730616/1408681982",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:05 +0000 2014",
+ "id": 505874883067129860,
+ "id_str": "505874883067129857",
+ "text": "ã€çŠ¶æ…‹è‰¯å¥½ã€‘ペンタックス・デジタル一眼レフカメラ・K20D 入札数=38 ç¾åœ¨ä¾¡æ ¼=15000円 http://t.co/4WK1f6V2n6終了=2014å¹´08月31æ—¥ 20:47:53 #一眼レフ http://t.co/PcSaXzfHMW",
+ "source": "<a href=\"https://github.com/AKB428/YahooAuctionBot\" rel=\"nofollow\">YahooAuction Degicame</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2278053589,
+ "id_str": "2278053589",
+ "name": "AuctionCamera",
+ "screen_name": "AuctionCamera",
+ "location": "",
+ "description": "Yahooオークションã®ãƒ‡ã‚¸ã‚«ãƒ¡ã‚«ãƒ†ã‚´ãƒªã‹ã‚‰å•†å“を抽出ã™ã‚‹ãƒœãƒƒãƒˆã§ã™ã€‚",
+ "url": "https://t.co/3sB1NDnd0m",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "https://t.co/3sB1NDnd0m",
+ "expanded_url": "https://github.com/AKB428/YahooAuctionBot",
+ "display_url": "github.com/AKB428/YahooAu…",
+ "indices": [
+ 0,
+ 23
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 5,
+ "friends_count": 24,
+ "listed_count": 0,
+ "created_at": "Sun Jan 05 20:10:56 +0000 2014",
+ "favourites_count": 1,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 199546,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/419927606146789376/vko-kd6Q_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/419927606146789376/vko-kd6Q_normal.jpeg",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [
+ {
+ "text": "一眼レフ",
+ "indices": [
+ 95,
+ 100
+ ]
+ }
+ ],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/4WK1f6V2n6",
+ "expanded_url": "http://atq.ck.valuecommerce.com/servlet/atq/referral?sid=2219441&pid=877510753&vcptn=auct/p/RJH492.PLqoLQQx1Jy8U9LE-&vc_url=http://page8.auctions.yahoo.co.jp/jp/auction/h192024356",
+ "display_url": "atq.ck.valuecommerce.com/servlet/atq/re…",
+ "indices": [
+ 49,
+ 71
+ ]
+ }
+ ],
+ "user_mentions": [],
+ "media": [
+ {
+ "id": 505874882828046340,
+ "id_str": "505874882828046336",
+ "indices": [
+ 101,
+ 123
+ ],
+ "media_url": "http://pbs.twimg.com/media/BwU6hpPCEAAxnpq.jpg",
+ "media_url_https": "https://pbs.twimg.com/media/BwU6hpPCEAAxnpq.jpg",
+ "url": "http://t.co/PcSaXzfHMW",
+ "display_url": "pic.twitter.com/PcSaXzfHMW",
+ "expanded_url": "http://twitter.com/AuctionCamera/status/505874883067129857/photo/1",
+ "type": "photo",
+ "sizes": {
+ "large": {
+ "w": 600,
+ "h": 450,
+ "resize": "fit"
+ },
+ "medium": {
+ "w": 600,
+ "h": 450,
+ "resize": "fit"
+ },
+ "thumb": {
+ "w": 150,
+ "h": 150,
+ "resize": "crop"
+ },
+ "small": {
+ "w": 340,
+ "h": 255,
+ "resize": "fit"
+ }
+ }
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:05 +0000 2014",
+ "id": 505874882995826700,
+ "id_str": "505874882995826689",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/yabai_giness\" rel=\"nofollow\">ヤãƒã™ãŽã‚‹!!ã‚®ãƒã‚¹ä¸–界記録</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2762405780,
+ "id_str": "2762405780",
+ "name": "ヤãƒã™ãŽã‚‹!!ã‚®ãƒã‚¹ä¸–界記録",
+ "screen_name": "yabai_giness",
+ "location": "",
+ "description": "世ã®ä¸­ã«ã¯ã€ã¾ã ã¾ã çŸ¥ã‚‰ã‚Œã¦ã„ãªã„スゴイ記録ãŒã‚ã‚‹ã‚“ã§ã™ï¼ \r\nãã‚“ãªã‚®ãƒã‚¹ä¸–界記録を見ã¤ã‘ã¾ã™â˜† \r\nã©ã‚“ã©ã‚“å‹é”ã«ã‚‚æ•™ãˆã¦ã‚ã’ã¦ãã ã•ã„ã­ww \r\nヤãƒã‚¤ã¨æ€ã£ãŸã‚‰ RT & フォローをã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 36,
+ "friends_count": 985,
+ "listed_count": 0,
+ "created_at": "Sun Aug 24 13:17:03 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 210,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503531782919045121/NiIC25wL_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503531782919045121/NiIC25wL_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2762405780/1408886328",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:05 +0000 2014",
+ "id": 505874882870009860,
+ "id_str": "505874882870009856",
+ "text": "ã™ã”ãé¢ç™½ã„夢見ãŸã€‚魔法科高校通ã£ã¦ã¦ï¼ˆåˆ¥ã«ä¸€ç§‘二科ã®åŒºåˆ¥ãªã„)クラスメイトã«ãƒ¨ã‚»ã‚¢ãƒ„メé¢å­ã‚„赤僕ã®æ‹“也ãŒã„ã¦ã€å­¦æ ¡å¯¾æŠ—åˆå”±ã‚³ãƒ³ã‚¯ãƒ¼ãƒ«ãŒé–‹å‚¬ã•ã‚ŒãŸã‚Šä¼šå ´å…¥ã‚Šã®éš›ä»–æ ¡ã®å¦¨å®³å·¥ä½œå—ã‘ãŸã‚Šã€æ‹“也ãŒé€£ã‚Œã¦ãã¦ãŸå®ŸãŒäººè³ªã«å–られãŸã‚Šã¨ã«ã‹ãã¦ã‚“ã“盛りã ã£ãŸæ¥½ã—ã‹ã£ãŸèµ¤åƒ•èª­ã¿ãŸã„手元ã«ãªã„",
+ "source": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 597357105,
+ "id_str": "597357105",
+ "name": "ãµã˜ã‚ˆã—",
+ "screen_name": "fuji_mark",
+ "location": "多摩動物公園",
+ "description": "æˆäººè…女å­",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 128,
+ "friends_count": 126,
+ "listed_count": 6,
+ "created_at": "Sat Jun 02 10:06:05 +0000 2012",
+ "favourites_count": 2842,
+ "utc_offset": 32400,
+ "time_zone": "Irkutsk",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 10517,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "0099B9",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme4/bg.gif",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme4/bg.gif",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503553738569560065/D_JW2dCJ_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503553738569560065/D_JW2dCJ_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/597357105/1408864355",
+ "profile_link_color": "0099B9",
+ "profile_sidebar_border_color": "5ED4DC",
+ "profile_sidebar_fill_color": "95E8EC",
+ "profile_text_color": "3C3940",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:05 +0000 2014",
+ "id": 505874882228281340,
+ "id_str": "505874882228281345",
+ "text": "RT @oen_yakyu: â—継続試åˆï¼ˆä¸­äº¬å¯¾å´‡å¾³ï¼‰46回~ 9時~\n 〈ラジオ中継〉\n らã˜ã‚‹â˜…らã˜ã‚‹â†’大阪放é€å±€ã‚’é¸æŠžâ†’NHK-FM\nâ—決å‹æˆ¦(三浦対中京or崇徳) 12時30分~\n 〈ラジオ中継〉\n らã˜ã‚‹â˜…らã˜ã‚‹â†’大阪放é€å±€ã‚’é¸æŠžâ†’NHK第一\n ※神奈å·ã®æ–¹ã¯æ™®é€šã®ãƒ©â€¦",
+ "source": "<a href=\"http://twicca.r246.jp/\" rel=\"nofollow\">twicca</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 18477566,
+ "id_str": "18477566",
+ "name": "Natit(ãªã¡ï¼‰ï¼ ãã†ã ã€ãƒˆãƒƒãƒ—è¡Œã“ã†",
+ "screen_name": "natit_yso",
+ "location": "ç¦å²¡å¸‚ã®ç«¯ã£ã“",
+ "description": "ヤー・ãƒãƒ£ã‚¤ã‚«ã€‚ç´«å®å‹¢ã®æœ«å¸­ãらã„ã§QMAã‚„ã£ã¦ã¾ã™ã€‚\r\n9/13(土)「ä¹å·žæ¯ã€ä»Šå¹´ã‚‚宜ã—ããŠé¡˜ã„ã—ã¾ã™ï¼ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã¯ã€Œãã†ã ã€ãƒˆãƒƒãƒ—ã€è¡Œã“ã†ã€‚ã€\r\nmore → http://t.co/ezuHyjF4Qy \r\nã€æ—…ã®äºˆå®šã€‘9/20-22 関西 → 9/23-28 北海é“ãã‚‹ã‚Š",
+ "url": "http://t.co/ll2yu78DGR",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/ll2yu78DGR",
+ "expanded_url": "http://qma-kyushu.sakura.ne.jp/",
+ "display_url": "qma-kyushu.sakura.ne.jp",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": [
+ {
+ "url": "http://t.co/ezuHyjF4Qy",
+ "expanded_url": "http://twpf.jp/natit_yso",
+ "display_url": "twpf.jp/natit_yso",
+ "indices": [
+ 83,
+ 105
+ ]
+ }
+ ]
+ }
+ },
+ "protected": false,
+ "followers_count": 591,
+ "friends_count": 548,
+ "listed_count": 93,
+ "created_at": "Tue Dec 30 14:11:44 +0000 2008",
+ "favourites_count": 11676,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 130145,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "131516",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif",
+ "profile_background_tile": true,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/1556202861/chibi-Leon_normal.jpg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/1556202861/chibi-Leon_normal.jpg",
+ "profile_link_color": "009999",
+ "profile_sidebar_border_color": "EEEEEE",
+ "profile_sidebar_fill_color": "EFEFEF",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sat Aug 30 23:12:39 +0000 2014",
+ "id": 505855649196953600,
+ "id_str": "505855649196953600",
+ "text": "â—継続試åˆï¼ˆä¸­äº¬å¯¾å´‡å¾³ï¼‰46回~ 9時~\n 〈ラジオ中継〉\n らã˜ã‚‹â˜…らã˜ã‚‹â†’大阪放é€å±€ã‚’é¸æŠžâ†’NHK-FM\nâ—決å‹æˆ¦(三浦対中京or崇徳) 12時30分~\n 〈ラジオ中継〉\n らã˜ã‚‹â˜…らã˜ã‚‹â†’大阪放é€å±€ã‚’é¸æŠžâ†’NHK第一\n ※神奈å·ã®æ–¹ã¯æ™®é€šã®ãƒ©ã‚¸ã‚ªã®NHK-FMã§ã‚‚",
+ "source": "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2761692762,
+ "id_str": "2761692762",
+ "name": "三浦学苑軟å¼é‡Žçƒéƒ¨å¿œæ´å›£ï¼",
+ "screen_name": "oen_yakyu",
+ "location": "",
+ "description": "兵庫県ã§é–‹å‚¬ã•ã‚Œã‚‹ã€Œã‚‚ã†ä¸€ã¤ã®ç”²å­åœ’ã€ã“ã¨å…¨å›½é«˜æ ¡è»Ÿå¼é‡Žçƒé¸æ‰‹æ¨©å¤§ä¼šã«å—é–¢æ±ãƒ–ロックã‹ã‚‰å‡ºå ´ã™ã‚‹ä¸‰æµ¦å­¦è‹‘軟å¼é‡Žçƒéƒ¨ã‚’å¿œæ´ã™ã‚‹éžå…¬å¼ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã™ã€‚",
+ "url": "http://t.co/Cn1tPTsBGY",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/Cn1tPTsBGY",
+ "expanded_url": "http://www.miura.ed.jp/index.html",
+ "display_url": "miura.ed.jp/index.html",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 464,
+ "friends_count": 117,
+ "listed_count": 4,
+ "created_at": "Sun Aug 24 07:47:29 +0000 2014",
+ "favourites_count": 69,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 553,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/504299474445811712/zsxJUmL0_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/504299474445811712/zsxJUmL0_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2761692762/1409069337",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 7,
+ "favorite_count": 2,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 7,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "oen_yakyu",
+ "name": "三浦学苑軟å¼é‡Žçƒéƒ¨å¿œæ´å›£ï¼",
+ "id": 2761692762,
+ "id_str": "2761692762",
+ "indices": [
+ 3,
+ 13
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:05 +0000 2014",
+ "id": 505874882110824450,
+ "id_str": "505874882110824448",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/sumahoanime\" rel=\"nofollow\">スマホã«å¯†å°â˜…アニメ画åƒ</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2725976444,
+ "id_str": "2725976444",
+ "name": "スマホã«å¯†å°â˜…アニメ画åƒ",
+ "screen_name": "sumahoanime",
+ "location": "",
+ "description": "ãªã‚“ã¨ã‚‚ã‚ãšã‚‰ã—ã„ã€ã„ã‚ã‚“ãªã‚­ãƒ£ãƒ©ãŒã‚¹ãƒžãƒ›ã«é–‰ã˜è¾¼ã‚られã¦ã„ã¾ã™ã€‚ \r\nã‚ãªãŸã®ã‚¹ãƒžãƒ›ã«ãƒžãƒƒãƒã™ã‚‹ç”»åƒãŒè¦‹ã¤ã‹ã‚‹ã‹ã‚‚♪ \r\næ°—ã«å…¥ã£ãŸã‚‰æ˜¯éž RT & フォローãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 227,
+ "friends_count": 1918,
+ "listed_count": 0,
+ "created_at": "Tue Aug 12 11:27:54 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 527,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/499155646164393984/l5vSz5zu_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/499155646164393984/l5vSz5zu_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2725976444/1407843121",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:05 +0000 2014",
+ "id": 505874881297133600,
+ "id_str": "505874881297133568",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/mijika_kiken\" rel=\"nofollow\">アナタã®ãã°ã®èº«è¿‘ãªå±é™º</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2713926078,
+ "id_str": "2713926078",
+ "name": "アナタã®ãã°ã®èº«è¿‘ãªå±é™º",
+ "screen_name": "mijika_kiken",
+ "location": "",
+ "description": "知らãªã„ã†ã¡ã«ã‚„ã£ã¦ã„ã‚‹å±é™ºãªè¡Œå‹•ã‚’見ã¤ã‘ã¦è‡ªåˆ†ã‚’守りã¾ã—ょã†ã€‚ å½¹ã«ç«‹ã¤ã¨æ€ã£ãŸã‚‰ RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 301,
+ "friends_count": 1871,
+ "listed_count": 0,
+ "created_at": "Thu Aug 07 07:12:50 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 644,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/497279579245907968/Ftvms_HR_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/497279579245907968/Ftvms_HR_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2713926078/1407395683",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:04 +0000 2014",
+ "id": 505874880294682600,
+ "id_str": "505874880294682624",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/ninkimono_daosy\" rel=\"nofollow\">人気者♥デイジー大好ã</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2726199583,
+ "id_str": "2726199583",
+ "name": "人気者♥デイジー大好ã",
+ "screen_name": "ninkimono_daosy",
+ "location": "",
+ "description": "デイジーã®æƒ³ã„ã‚’ã€ä»£ã‚ã‚Šã«ã¤ã¶ã‚„ãã¾ã™â™ª \r\nデイジーã®ã‹ã‚ã„ã„ç”»åƒã‚„グッズも大好ãï½— \r\nå¯æ„›ã„ã¨æ€ã£ãŸã‚‰ RT & フォローãŠé¡˜ã„ã—ã¾ã™ã€‚ \r\n「éžå…¬å¼ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã™ã€",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 190,
+ "friends_count": 474,
+ "listed_count": 0,
+ "created_at": "Tue Aug 12 12:58:33 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 469,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/499178622494576640/EzWKdR_p_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/499178622494576640/EzWKdR_p_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2726199583/1407848478",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:04 +0000 2014",
+ "id": 505874879392919550,
+ "id_str": "505874879392919552",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/shiawasehanashi\" rel=\"nofollow\">幸ã›è©±ã§ãƒ•ãƒ«å……é›»ã—よã†</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2721453846,
+ "id_str": "2721453846",
+ "name": "幸ã›è©±ã§ãƒ•ãƒ«å……é›»ã—よã†ww",
+ "screen_name": "shiawasehanashi",
+ "location": "",
+ "description": "ç§ãŒèžã„ã¦å¿ƒã«æ®‹ã£ãŸæ„Ÿå‹•ã‚¨ãƒ”ソードをãŠå±Šã‘ã—ã¾ã™ã€‚\r\nå°‘ã—ã§ã‚‚多ãã®äººã¸å±Šã‘ãŸã„ã¨æ€ã„ã¾ã™ã€‚\r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & フォローãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 302,
+ "friends_count": 1886,
+ "listed_count": 0,
+ "created_at": "Sun Aug 10 12:16:25 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 508,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/498444554916216832/ml8EiQka_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/498444554916216832/ml8EiQka_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2721453846/1407673555",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:04 +0000 2014",
+ "id": 505874879103520800,
+ "id_str": "505874879103520768",
+ "text": "RT @Ang_Angel73: 逢å‚「ãã£â€¦åƒ•ã®ç§˜ã‚られã—å³ç›®ãŒâ€¦ï¼ã€\n一åŒã€Œâ€¦â€¦â€¦â€¦â€¦ã€‚ã€",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2571968509,
+ "id_str": "2571968509",
+ "name": "イイヒト",
+ "screen_name": "IwiAlohomora",
+ "location": "è‰è‘‰ã®é™°",
+ "description": "大人ã§ã™ã€‚気軽ã«çµ¡ã‚“ã§ãれるã¨ã†ã‚Œã—ã„ã§ã™ï¼ イラスト大好ãï¼ï¼ˆâ‰§âˆ‡â‰¦ï¼‰ BF(仮)逢å‚紘夢ãã‚“ã«ãŠç†±ã§ã™ï¼ マンガも好ã♡欲望ã®ã¾ã¾ã«ã¤ã¶ã‚„ãã¾ã™ã®ã§ã”注æ„を。雑食♡",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 156,
+ "friends_count": 165,
+ "listed_count": 14,
+ "created_at": "Tue Jun 17 01:18:34 +0000 2014",
+ "favourites_count": 11926,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 7234,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/504990074862178304/DoBvOb9c_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/504990074862178304/DoBvOb9c_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2571968509/1409106012",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:27:01 +0000 2014",
+ "id": 505874364596621300,
+ "id_str": "505874364596621313",
+ "text": "逢å‚「ãã£â€¦åƒ•ã®ç§˜ã‚られã—å³ç›®ãŒâ€¦ï¼ã€\n一åŒã€Œâ€¦â€¦â€¦â€¦â€¦ã€‚ã€",
+ "source": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1600750194,
+ "id_str": "1600750194",
+ "name": "臙脂",
+ "screen_name": "Ang_Angel73",
+ "location": "逢å‚紘夢ã®ãã°ã«",
+ "description": "自由ã€æ°—ã¾ã¾ã«ã€‚詳ã—ãã¯ãƒ„イプロ。アイコンã¯ã¾ã‚ã›ã‚ã‚Šã¡ã‚ƒã‚“ã‹ã‚‰ã ã‚ˆâ˜†ï½žï¼ˆã‚。∂)",
+ "url": "http://t.co/kKCCwHTaph",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/kKCCwHTaph",
+ "expanded_url": "http://twpf.jp/Ang_Angel73",
+ "display_url": "twpf.jp/Ang_Angel73",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 155,
+ "friends_count": 154,
+ "listed_count": 10,
+ "created_at": "Wed Jul 17 11:44:31 +0000 2013",
+ "favourites_count": 2115,
+ "utc_offset": 32400,
+ "time_zone": "Irkutsk",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 12342,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/378800000027871001/aa764602922050b22bf9ade3741367dc.jpeg",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/378800000027871001/aa764602922050b22bf9ade3741367dc.jpeg",
+ "profile_background_tile": true,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/500293786287603713/Ywyh69eG_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/500293786287603713/Ywyh69eG_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1600750194/1403879183",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "FFFFFF",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 2,
+ "favorite_count": 2,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 2,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "Ang_Angel73",
+ "name": "臙脂",
+ "id": 1600750194,
+ "id_str": "1600750194",
+ "indices": [
+ 3,
+ 15
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:04 +0000 2014",
+ "id": 505874877933314050,
+ "id_str": "505874877933314048",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/honne_jyoshi1\" rel=\"nofollow\">秘密ã®æœ¬éŸ³â™¥å¥³å­ç·¨</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2762237088,
+ "id_str": "2762237088",
+ "name": "秘密ã®æœ¬éŸ³â™¥å¥³å­ç·¨",
+ "screen_name": "honne_jyoshi1",
+ "location": "",
+ "description": "普段ã¯è¨€ãˆãªã„「ãŠãƒ»ã‚“・ãªã®å»ºå‰ã¨æœ¬éŸ³ã€ã‚’ã¤ã¶ã‚„ãã¾ã™ã€‚ æ°—ã«ãªã‚‹ ã‚ã®äººã®æœ¬éŸ³ã‚‚ã€ã‚ã‹ã‚‹ã‹ã‚‚!? \r\nã‚ã‹ã‚‹ã£ã¦äººã¯ RT & フォローをã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 123,
+ "friends_count": 988,
+ "listed_count": 0,
+ "created_at": "Sun Aug 24 12:27:07 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 211,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503519190364332032/BVjS_XBD_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503519190364332032/BVjS_XBD_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2762237088/1408883328",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:04 +0000 2014",
+ "id": 505874877148958700,
+ "id_str": "505874877148958721",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/bi_iroenpitu\" rel=\"nofollow\">美ã—éŽãŽã‚‹â˜…色鉛筆アート</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2740047343,
+ "id_str": "2740047343",
+ "name": "美ã—éŽãŽã‚‹â˜…色鉛筆アート",
+ "screen_name": "bi_iroenpitu",
+ "location": "",
+ "description": "ã»ã‚“ã¨ã«ã‚³ãƒ¬è‰²é‰›ç­†ãªã®ï½žï¼Ÿ \r\n本物ã¨è¦‹é–“é•ãˆã‚‹ç¨‹ã®ãƒªã‚¢ãƒªãƒ†ã‚£ã‚’御覧ãã ã•ã„。 \r\næ°—ã«å…¥ã£ãŸã‚‰ RT & 相互フォローãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 321,
+ "friends_count": 1990,
+ "listed_count": 0,
+ "created_at": "Sun Aug 17 16:15:05 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 396,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501039950972739585/isigil4V_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501039950972739585/isigil4V_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2740047343/1408292283",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:03 +0000 2014",
+ "id": 505874876465295360,
+ "id_str": "505874876465295361",
+ "text": "ã€H15-9-4】é“路を利用ã™ã‚‹åˆ©ç›Šã¯å射的利益ã§ã‚ã‚Šã€å»ºç¯‰åŸºæº–法ã«åŸºã¥ã„ã¦é“路一ã®æŒ‡å®šãŒãªã•ã‚Œã¦ã„ã‚‹ç§é“ã®æ•·åœ°æ‰€æœ‰è€…ã«å¯¾ã—ã€é€šè¡Œå¦¨å®³è¡Œç‚ºã®æŽ’除を求ã‚る人格的権利をèªã‚ã‚‹ã“ã¨ã¯ã§ããªã„。→誤。",
+ "source": "<a href=\"http://twittbot.net/\" rel=\"nofollow\">twittbot.net</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1886570281,
+ "id_str": "1886570281",
+ "name": "行政法éŽåŽ»å•",
+ "screen_name": "gyosei_goukaku",
+ "location": "",
+ "description": "行政書士ã®æœ¬è©¦é¨“å•é¡Œã®éŽåŽ»å•ï¼ˆè¡Œæ”¿æ³•åˆ†é‡Žï¼‰ã‚’ランダムã«ã¤ã¶ã‚„ãã¾ã™ã€‚å•é¡Œã¯éšæ™‚追加中ã§ã™ã€‚基本的ã«ç›¸äº’フォローã—ã¾ã™ã€‚※140字制é™ã®éƒ½åˆä¸Šã€è¡¨ç¾ã¯ä¸€éƒ¨å¤‰ãˆã¦ã‚ã‚Šã¾ã™ã€‚解説も文字数ãŒå¯èƒ½ã§ã‚ã‚Œã°ãªã‚‹ã¹ã…。",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 1554,
+ "friends_count": 1772,
+ "listed_count": 12,
+ "created_at": "Fri Sep 20 13:24:29 +0000 2013",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 14565,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/378800000487791870/0e45e3c089c6b641cdd8d1b6f1ceb8a4_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000487791870/0e45e3c089c6b641cdd8d1b6f1ceb8a4_normal.jpeg",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:03 +0000 2014",
+ "id": 505874876318511100,
+ "id_str": "505874876318511104",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/kgoehassou\" rel=\"nofollow\">K点越ãˆã®ç™ºæƒ³åŠ›!!</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2744863153,
+ "id_str": "2744863153",
+ "name": "K点越ãˆã®ç™ºæƒ³åŠ›!!",
+ "screen_name": "kgoehassou",
+ "location": "",
+ "description": "ã„ã£ãŸã„ã©ã†ã‚„ã£ãŸã‚‰ã€ãã®é ˜åŸŸã«ãŸã©ã‚Šã¤ã‘ã‚‹ã®ã‹ï¼ï¼Ÿ \r\nãã‚“ãªæ€ã‚ãšç¬‘ã£ã¦ã—ã¾ã†åˆ¥ä¸–ç•Œã®ç™ºæƒ³åŠ›ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nãŠã‚‚ã—ã‚ã‹ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 76,
+ "friends_count": 957,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 13:00:08 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 341,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501715651686178816/Fgpe0B8M_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501715651686178816/Fgpe0B8M_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744863153/1408453328",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:03 +0000 2014",
+ "id": 505874875521581060,
+ "id_str": "505874875521581056",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/ketueki_sinjitu\" rel=\"nofollow\">血液型ã®çœŸå®Ÿï¼’</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2698625690,
+ "id_str": "2698625690",
+ "name": "血液型ã®çœŸå®Ÿ",
+ "screen_name": "ketueki_sinjitu",
+ "location": "",
+ "description": "ã‚„ã£ã±ã‚Šãã†ã ã£ãŸã®ã‹ï½žâ™ª\r\næ„外ãªã€ã‚ã®äººã®è£å´ã‚’見ã¤ã‘ã¾ã™ã€‚\r\né¢ç™½ã‹ã£ãŸã‚‰RT & 相互フォローã§ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 193,
+ "friends_count": 1785,
+ "listed_count": 1,
+ "created_at": "Fri Aug 01 16:11:40 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 769,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/495241446706790400/h_0DSFPG_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/495241446706790400/h_0DSFPG_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2698625690/1406911319",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:03 +0000 2014",
+ "id": 505874874712072200,
+ "id_str": "505874874712072192",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/yahari_kamiga\" rel=\"nofollow\">ã‚„ã£ã±ã‚Šç¥žãŒï¼Ÿï¼Ÿã‚’作る時</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2714868440,
+ "id_str": "2714868440",
+ "name": "ã‚„ã£ã±ã‚Šç¥žãŒï¼Ÿï¼Ÿã‚’作る時",
+ "screen_name": "yahari_kamiga",
+ "location": "",
+ "description": "ã‚„ã£ã±ã‚Šä»Šæ—¥ã‚‚ã€ç¥žã¯ä½•ã‹ã‚’作ã‚ã†ã¨ã—ã¦ã„ã¾ã™ã€€ç¬‘。 ã©ã†ã‚„ã£ã¦ä½œã£ã¦ã„ã‚‹ã®ã‹ã‚ã‹ã£ãŸã‚‰ RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 243,
+ "friends_count": 1907,
+ "listed_count": 0,
+ "created_at": "Thu Aug 07 16:12:33 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 590,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/497416102108884992/NRMEbKaT_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/497416102108884992/NRMEbKaT_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2714868440/1407428237",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:03 +0000 2014",
+ "id": 505874874275864600,
+ "id_str": "505874874275864576",
+ "text": "RT @takuramix: ç¦å³¶ç¬¬ä¸€åŽŸç™ºã®æ§‹å†…地図ãŒã“ã¡ã‚‰ã€‚\nhttp://t.co/ZkU4TZCGPG\nã©ã†è¦‹ã¦ã‚‚ã€ï¼‘å·æ©Ÿã€‚\nRT @Lightworker19: ã€å¤§æ‹¡æ•£ã€‘  ç¦å³¶ç¬¬ä¸€åŽŸç™ºã€€ï¼”å·æ©Ÿã€€çˆ†ç™ºå‹•ç”»ã€€40秒~  http://t.co/lmlgp38fgZ",
+ "source": "<a href=\"http://twitter.softama.com/\" rel=\"nofollow\">ツイタマ</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 62525372,
+ "id_str": "62525372",
+ "name": "NANCY-MOON☆ã²ã‚ˆã“ã¡ã‚ƒã‚“☆",
+ "screen_name": "nancy_moon_703",
+ "location": "JAPAN",
+ "description": "ã€ç„¡æ–­è»¢è¼‰ç¦æ­¢ï½¥ã‚³ãƒ”ペç¦æ­¢ãƒ»éžå…¬å¼RTç¦æ­¢ã€‘ã€å¿…読ï¼ã€‘⇒ http://t.co/nuUvfUVD 今ç¾åœ¨æ´»å‹•ä¸­ã®æ±æ–¹ç¥žèµ·YUNHO&CHANGMINã®2人を全力ã§å¿œæ´ã—ã¦ã„ã¾ã™!!(^_-)-☆ ※æ±æ–¹ç¥žèµ·åŠã³YUNHO&CHANGMINã‚’å¿œæ´ã—ã¦ã„ãªã„方・éµä»˜ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ•ã‚©ãƒ­ãƒ¼ãŠæ–­ã‚Šï¼",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": [
+ {
+ "url": "http://t.co/nuUvfUVD",
+ "expanded_url": "http://goo.gl/SrGLb",
+ "display_url": "goo.gl/SrGLb",
+ "indices": [
+ 29,
+ 49
+ ]
+ }
+ ]
+ }
+ },
+ "protected": false,
+ "followers_count": 270,
+ "friends_count": 328,
+ "listed_count": 4,
+ "created_at": "Mon Aug 03 14:22:24 +0000 2009",
+ "favourites_count": 3283,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 180310,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "642D8B",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/470849781397336064/ltM6EdFn.jpeg",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/470849781397336064/ltM6EdFn.jpeg",
+ "profile_background_tile": true,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/3699005246/9ba2e306518d296b68b7cbfa5e4ce4e6_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/3699005246/9ba2e306518d296b68b7cbfa5e4ce4e6_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/62525372/1401094223",
+ "profile_link_color": "FF0000",
+ "profile_sidebar_border_color": "FFFFFF",
+ "profile_sidebar_fill_color": "F065A8",
+ "profile_text_color": "080808",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sat Aug 30 21:21:33 +0000 2014",
+ "id": 505827689660313600,
+ "id_str": "505827689660313600",
+ "text": "ç¦å³¶ç¬¬ä¸€åŽŸç™ºã®æ§‹å†…地図ãŒã“ã¡ã‚‰ã€‚\nhttp://t.co/ZkU4TZCGPG\nã©ã†è¦‹ã¦ã‚‚ã€ï¼‘å·æ©Ÿã€‚\nRT @Lightworker19: ã€å¤§æ‹¡æ•£ã€‘  ç¦å³¶ç¬¬ä¸€åŽŸç™ºã€€ï¼”å·æ©Ÿã€€çˆ†ç™ºå‹•ç”»ã€€40秒~  http://t.co/lmlgp38fgZ",
+ "source": "<a href=\"https://about.twitter.com/products/tweetdeck\" rel=\"nofollow\">TweetDeck</a>",
+ "truncated": false,
+ "in_reply_to_status_id": 505774460910043140,
+ "in_reply_to_status_id_str": "505774460910043136",
+ "in_reply_to_user_id": 238157843,
+ "in_reply_to_user_id_str": "238157843",
+ "in_reply_to_screen_name": "Lightworker19",
+ "user": {
+ "id": 29599253,
+ "id_str": "29599253",
+ "name": "タクラミックス",
+ "screen_name": "takuramix",
+ "location": "i7",
+ "description": "ç§ã®æ©Ÿèƒ½ä¸€è¦§ï¼šæ­Œã†ã€æ¼”劇ã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¨ãƒ³ã‚¸ãƒ‹ã‚¢ã€ãƒ©ã‚¤ã‚¿ãƒ¼ã€ãƒ—ログラマã€ç¿»è¨³ã€ã‚·ãƒ«ãƒãƒ¼ã‚¢ã‚¯ã‚»ã‚µãƒªã€â€¦â€¦ä½•ã‚’ã‚„ã£ã¦ã‚‹äººã‹ã¯è‰¯ãã‚ã‹ã‚‰ãªã„人ãªã®ã§ã€ã€Œæ©Ÿèƒ½ã€ãŒæ¬²ã—ã„人ã¯ç§ã«ãŒã£ã‹ã‚Šã™ã‚‹ã§ã—ょã†ã€‚ç§ã£ã¦äººé–“ã«å¾¡ç”¨ãŒã‚ã‚‹ãªã‚‰åˆ¥ã§ã™ãŒã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 5136,
+ "friends_count": 724,
+ "listed_count": 335,
+ "created_at": "Wed Apr 08 01:10:58 +0000 2009",
+ "favourites_count": 21363,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 70897,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/2049751947/takuramix1204_normal.jpg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/2049751947/takuramix1204_normal.jpg",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 1,
+ "favorite_count": 1,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/ZkU4TZCGPG",
+ "expanded_url": "http://www.tepco.co.jp/nu/fukushima-np/review/images/review1_01.gif",
+ "display_url": "tepco.co.jp/nu/fukushima-n…",
+ "indices": [
+ 17,
+ 39
+ ]
+ },
+ {
+ "url": "http://t.co/lmlgp38fgZ",
+ "expanded_url": "http://youtu.be/gDXEhyuVSDk",
+ "display_url": "youtu.be/gDXEhyuVSDk",
+ "indices": [
+ 99,
+ 121
+ ]
+ }
+ ],
+ "user_mentions": [
+ {
+ "screen_name": "Lightworker19",
+ "name": "Lightworker",
+ "id": 238157843,
+ "id_str": "238157843",
+ "indices": [
+ 54,
+ 68
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ "retweet_count": 1,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/ZkU4TZCGPG",
+ "expanded_url": "http://www.tepco.co.jp/nu/fukushima-np/review/images/review1_01.gif",
+ "display_url": "tepco.co.jp/nu/fukushima-n…",
+ "indices": [
+ 32,
+ 54
+ ]
+ },
+ {
+ "url": "http://t.co/lmlgp38fgZ",
+ "expanded_url": "http://youtu.be/gDXEhyuVSDk",
+ "display_url": "youtu.be/gDXEhyuVSDk",
+ "indices": [
+ 114,
+ 136
+ ]
+ }
+ ],
+ "user_mentions": [
+ {
+ "screen_name": "takuramix",
+ "name": "タクラミックス",
+ "id": 29599253,
+ "id_str": "29599253",
+ "indices": [
+ 3,
+ 13
+ ]
+ },
+ {
+ "screen_name": "Lightworker19",
+ "name": "Lightworker",
+ "id": 238157843,
+ "id_str": "238157843",
+ "indices": [
+ 69,
+ 83
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:03 +0000 2014",
+ "id": 505874873961308160,
+ "id_str": "505874873961308160",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/anayuki_suki\" rel=\"nofollow\">ã‚„ã£ã±ã‚Šã‚¢ãƒŠé›ªãŒå¥½ã♥</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2714052962,
+ "id_str": "2714052962",
+ "name": "ã‚„ã£ã±ã‚Šã‚¢ãƒŠé›ªãŒå¥½ã♥",
+ "screen_name": "anayuki_suki",
+ "location": "",
+ "description": "ãªã‚“ã ã‹ã‚“ã è¨€ã£ã¦ã‚‚ã‚„ã£ã±ã‚Šã‚¢ãƒŠé›ªãŒå¥½ããªã‚“ã§ã™ã‚ˆã­ï½žâ™ª \r\nç§ã‚‚好ãã£ã¦äººã¯ RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 368,
+ "friends_count": 1826,
+ "listed_count": 1,
+ "created_at": "Thu Aug 07 08:29:13 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 670,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/497299646662705153/KMo3gkv7_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/497299646662705153/KMo3gkv7_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2714052962/1407400477",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "zh"
+ },
+ "created_at": "Sun Aug 31 00:29:03 +0000 2014",
+ "id": 505874873759977500,
+ "id_str": "505874873759977473",
+ "text": "å››å·ç›†åœ°æ±Ÿæ·®ç­‰åœ°å°†æœ‰å¼ºé™é›¨ 开学日多地将有雨:   中新网8月31日电 æ®ä¸­å¤®æ°”象å°æ¶ˆæ¯ï¼Œæ±Ÿæ·®ä¸œéƒ¨ã€å››å·ç›†åœ°ä¸œåŒ—部等地今天(31æ—¥)åˆå°†è¿Žæ¥ä¸€åœºæš´é›¨æˆ–大暴雨天气。明天9月1日,是中å°å­¦ç”Ÿå¼€å­¦çš„æ—¥å­ã€‚预计明天,内蒙å¤ä¸­éƒ¨ã€... http://t.co/toQgVlXPyH",
+ "source": "<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2281979863,
+ "id_str": "2281979863",
+ "name": "News 24h China",
+ "screen_name": "news24hchn",
+ "location": "",
+ "description": "",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 719,
+ "friends_count": 807,
+ "listed_count": 7,
+ "created_at": "Wed Jan 08 10:56:04 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": 7200,
+ "time_zone": "Amsterdam",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 94782,
+ "lang": "it",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/452558963754561536/QPID3isM.jpeg",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/452558963754561536/QPID3isM.jpeg",
+ "profile_background_tile": true,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/439031926569979904/SlBH9iMg_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/439031926569979904/SlBH9iMg_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2281979863/1393508427",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "FFFFFF",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/toQgVlXPyH",
+ "expanded_url": "http://news24h.allnews24h.com/FX54",
+ "display_url": "news24h.allnews24h.com/FX54",
+ "indices": [
+ 114,
+ 136
+ ]
+ }
+ ],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "zh"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:03 +0000 2014",
+ "id": 505874873248268300,
+ "id_str": "505874873248268288",
+ "text": "@Take3carnifex ãã‚Œã¯å¤§å¤‰ï¼ä¸€å¤§äº‹ï¼å‘½ã«é–¢ã‚ã‚Šã¾ã™ï¼\n是éžã†ã¡ã«å—診ã—ã¦ä¸‹ã•ã„ï¼",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": 505874353716600800,
+ "in_reply_to_status_id_str": "505874353716600832",
+ "in_reply_to_user_id": 535179785,
+ "in_reply_to_user_id_str": "535179785",
+ "in_reply_to_screen_name": "Take3carnifex",
+ "user": {
+ "id": 226897125,
+ "id_str": "226897125",
+ "name": "ã²ã‹ã‚Šï¼ hack",
+ "screen_name": "hikari_thirteen",
+ "location": "",
+ "description": "hackã¨ã„ã†ãƒãƒ³ãƒ‰ã§ã€ã‚®ã‚¿ãƒ¼ã‚’å¼¾ã„ã¦ã„ã¾ã™ã€‚ モンãƒãƒ³ã¨ãƒã‚±ãƒ¢ãƒ³ãŒå¥½ã。 \nSPRING WATER リードギター(ヘルプ)\nROCK OUT レギュラーDJ",
+ "url": "http://t.co/SQLZnvjVxB",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/SQLZnvjVxB",
+ "expanded_url": "http://s.ameblo.jp/hikarihikarimay",
+ "display_url": "s.ameblo.jp/hikarihikarimay",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 296,
+ "friends_count": 348,
+ "listed_count": 3,
+ "created_at": "Wed Dec 15 10:51:51 +0000 2010",
+ "favourites_count": 33,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 3293,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "131516",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif",
+ "profile_background_tile": true,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/378800000504584690/8ccba98eda8c0fd1d15a74e401f621d1_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000504584690/8ccba98eda8c0fd1d15a74e401f621d1_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/226897125/1385551752",
+ "profile_link_color": "009999",
+ "profile_sidebar_border_color": "EEEEEE",
+ "profile_sidebar_fill_color": "EFEFEF",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "Take3carnifex",
+ "name": "Take3",
+ "id": 535179785,
+ "id_str": "535179785",
+ "indices": [
+ 0,
+ 14
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:03 +0000 2014",
+ "id": 505874873223110660,
+ "id_str": "505874873223110656",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/imadokijoshiko\" rel=\"nofollow\">今ã©ã女å­é«˜ç”Ÿã®è¬Žw</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2744236873,
+ "id_str": "2744236873",
+ "name": "今ã©ã女å­é«˜ç”Ÿã®è¬Žw",
+ "screen_name": "imadokijoshiko",
+ "location": "",
+ "description": "æ€ã‚ãšè€³ã‚’ç–‘ã†ç”·æ€§ã®æ–¹ã®å¤¢ã‚’壊ã—ã¦ã—ã¾ã†ã€\r\n女å­é«˜ç”Ÿé”ã®ãƒ‡ã‚£ãƒ¼ãƒ—ãªä¸–界を見ã¦ãã ã•ã„☆ \r\nãŠã‚‚ã—ã‚ã„ã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 79,
+ "friends_count": 973,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 07:06:47 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 354,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501627015980535808/avWBgkDh_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501627015980535808/avWBgkDh_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744236873/1408432455",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:02 +0000 2014",
+ "id": 505874872463925250,
+ "id_str": "505874872463925248",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/risou_dansei\" rel=\"nofollow\">ç§ã®ç†æƒ³ã®ç”·æ€§åƒ</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2761782601,
+ "id_str": "2761782601",
+ "name": "ç§ã®ç†æƒ³ã®ç”·æ€§åƒ",
+ "screen_name": "risou_dansei",
+ "location": "",
+ "description": "ã“ã‚“ãªç”·æ€§â™¥ ã»ã‚“ã¨ã«ã„ã‚‹ã®ã‹ã—ら!? \r\n「ã„ãŸã‚‰ã„ã„ã®ã«ãªãã€ã£ã¦ã„ã†ç†æƒ³ã®ç”·æ€§åƒã‚’ã‚’ã€ç§ç›®ç·šã§ã¤ã¶ã‚„ãã¾ã™ã€‚ \r\nã„ã„ãªã¨æ€ã£ãŸäººã¯ RT & フォローãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 69,
+ "friends_count": 974,
+ "listed_count": 0,
+ "created_at": "Sun Aug 24 08:03:32 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 208,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503452833719410688/tFU509Yk_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503452833719410688/tFU509Yk_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2761782601/1408867519",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:02 +0000 2014",
+ "id": 505874871713157100,
+ "id_str": "505874871713157120",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/gekiatu_6byou\" rel=\"nofollow\">激アツ★6秒動画</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2725690658,
+ "id_str": "2725690658",
+ "name": "激アツ★6秒動画",
+ "screen_name": "gekiatu_6byou",
+ "location": "",
+ "description": "話題ã®ï¼–ç§’å‹•ç”»ï¼ \r\næ€ã‚ãšã€Œã»ã‚“ã¨ã‹ã‚ˆã£ã€ã¦ãƒ„ッコんã§ã—ã¾ã†å†…容ã®ã‚ªãƒ³ãƒ‘ãƒ¬ãƒ¼ãƒ‰ï¼ \r\nãŠã‚‚ã—ã‚ã‹ã£ãŸã‚‰ã€æ˜¯éž RT & フォローãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 195,
+ "friends_count": 494,
+ "listed_count": 0,
+ "created_at": "Tue Aug 12 08:17:29 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 477,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/499107997444886528/3rl6FrIk_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/499107997444886528/3rl6FrIk_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2725690658/1407832963",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:02 +0000 2014",
+ "id": 505874871616671740,
+ "id_str": "505874871616671744",
+ "text": "爆笑wwç解答集ï¼\n先生ã®ãƒ„メã®ç”˜ã•ã¨ç”Ÿå¾’ã®ã‚»ãƒ³ã‚¹ã‚’æ„Ÿã˜ã‚‹ä¸€å•ä¸€ç­”ã ã¨FBã§ã‚‚話題ï¼ï¼\nã†ã©ã‚“天下一決定戦ウィンドウズ9三é‡é«˜æ ¡ç«¹å†…ç”±æµã‚¢ãƒŠèŠ±ç«ä¿é™º\nhttp://t.co/jRWJt8IrSB http://t.co/okrAoxSbt0",
+ "source": "<a href=\"https://twitter.com/waraeru_kan\" rel=\"nofollow\">笑ãˆã‚‹åšç‰©é¤¨</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2748747362,
+ "id_str": "2748747362",
+ "name": "笑ãˆã‚‹åšç‰©é¤¨",
+ "screen_name": "waraeru_kan",
+ "location": "",
+ "description": "",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 19,
+ "friends_count": 10,
+ "listed_count": 0,
+ "created_at": "Wed Aug 20 11:11:04 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 15137,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://abs.twimg.com/sticky/default_profile_images/default_profile_4_normal.png",
+ "profile_image_url_https": "https://abs.twimg.com/sticky/default_profile_images/default_profile_4_normal.png",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": true,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/jRWJt8IrSB",
+ "expanded_url": "http://bit.ly/1qBa1nl",
+ "display_url": "bit.ly/1qBa1nl",
+ "indices": [
+ 75,
+ 97
+ ]
+ }
+ ],
+ "user_mentions": [],
+ "media": [
+ {
+ "id": 505874871344066560,
+ "id_str": "505874871344066560",
+ "indices": [
+ 98,
+ 120
+ ],
+ "media_url": "http://pbs.twimg.com/media/BwU6g-dCcAALxAW.png",
+ "media_url_https": "https://pbs.twimg.com/media/BwU6g-dCcAALxAW.png",
+ "url": "http://t.co/okrAoxSbt0",
+ "display_url": "pic.twitter.com/okrAoxSbt0",
+ "expanded_url": "http://twitter.com/waraeru_kan/status/505874871616671744/photo/1",
+ "type": "photo",
+ "sizes": {
+ "small": {
+ "w": 340,
+ "h": 425,
+ "resize": "fit"
+ },
+ "thumb": {
+ "w": 150,
+ "h": 150,
+ "resize": "crop"
+ },
+ "large": {
+ "w": 600,
+ "h": 750,
+ "resize": "fit"
+ },
+ "medium": {
+ "w": 600,
+ "h": 750,
+ "resize": "fit"
+ }
+ }
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:02 +0000 2014",
+ "id": 505874871268540400,
+ "id_str": "505874871268540416",
+ "text": "@nasan_arai \nåå‰â†’ãªãƒ¼ã•ã‚“\n第一å°è±¡â†’誰。(´・_ï½¥`)\n今ã®å°è±¡â†’ã‚Œã„ら♡\nLINE交æ›ã§ãる?→ã—ã¦ã‚‹(「・ω・)ï½¢\n好ããªã¨ã“ã‚→å¯æ„›ã„優ã—ã„優ã—ã„優ã—ã„\n最後ã«ä¸€è¨€â†’ãªãƒ¼ã•ã‚“好ã〜(´・_ï½¥`)♡GEMç¾å ´ãŠã„ã§ã­(´・_ï½¥`)♡\n\n#ãµãã¼ã—ãŸäººã«ã‚„ã‚‹",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": 1717603286,
+ "in_reply_to_user_id_str": "1717603286",
+ "in_reply_to_screen_name": "nasan_arai",
+ "user": {
+ "id": 2417626784,
+ "id_str": "2417626784",
+ "name": "✩.ゆãଘ(*´꒳`)",
+ "screen_name": "Ymaaya_gem",
+ "location": "",
+ "description": "â½â½Ù©( á– )Û¶â¾â¾ â¤ï¸Ž æ­¦ ç”° 舞 彩 â¤ï¸Ž â‚â‚Ù©( á› )۶₎₎",
+ "url": "http://t.co/wR0Qb76TbB",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/wR0Qb76TbB",
+ "expanded_url": "http://twpf.jp/Ymaaya_gem",
+ "display_url": "twpf.jp/Ymaaya_gem",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 198,
+ "friends_count": 245,
+ "listed_count": 1,
+ "created_at": "Sat Mar 29 16:03:06 +0000 2014",
+ "favourites_count": 3818,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": true,
+ "verified": false,
+ "statuses_count": 8056,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/505516858816987136/4gFGjHzu_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/505516858816987136/4gFGjHzu_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2417626784/1407764793",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [
+ {
+ "text": "ãµãã¼ã—ãŸäººã«ã‚„ã‚‹",
+ "indices": [
+ 128,
+ 138
+ ]
+ }
+ ],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "nasan_arai",
+ "name": "ãªãƒ¼ã•ã‚“",
+ "id": 1717603286,
+ "id_str": "1717603286",
+ "indices": [
+ 0,
+ 11
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:02 +0000 2014",
+ "id": 505874871218225150,
+ "id_str": "505874871218225152",
+ "text": "\"ソードマスター\"剣è–カミイズミ (CV:ç·‘å·å…‰)-「ソードマスターã€ã®ã‚¢ã‚¹ã‚¿ãƒªã‚¹ã‚¯æ‰€æŒè€…\n第一師団団長ã«ã—ã¦ã€Œå‰£è–ã€ã®ç§°å·ã‚’æŒã¤å‰£å£«ã€‚イデアã®å‰£ã®å¸«åŒ ã€‚ \n敵味方ã‹ã‚‰ã‚‚尊敬ã•ã‚Œã‚‹ä¸€æµã®æ­¦äººã€‚",
+ "source": "<a href=\"http://twittbot.net/\" rel=\"nofollow\">twittbot.net</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1435517814,
+ "id_str": "1435517814",
+ "name": "俺ã€é–¢ä¿‚ãªã„よ?",
+ "screen_name": "BDFF_LOVE",
+ "location": "ルクセンダルクorリングアベルã•ã‚“ã®éš£",
+ "description": "自分ãªã‚Šã«ç”Ÿãる人ã€æœ€å¾Œã¾ã§ã‚ãらã‚ãªã„ã®ã€‚ã§ã‚‚ã€ãƒ•ã‚©ãƒ­ãƒ¼ã‚ã‚ŠãŒã¨ã†â€¦ã€‚@ringo_BDFFLOVE â†ã¯ã€å¦¹ã§ã™ã€‚時々ã€ä¼šè©±ã—ã¾ã™ã€‚「ç¾åœ¨BOTã§ã€BDFFã®ã“ã¨å‘Ÿãよï¼ã€å¤œã¯ã€å…¨æ»… 「BDFFプレイ中ã€è©³ã—ãã¯ã€ãƒ„イプロã¿ã¦ãã ã•ã„ï¼(絶対)",
+ "url": "http://t.co/5R4dzpbWX2",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/5R4dzpbWX2",
+ "expanded_url": "http://twpf.jp/BDFF_LOVE",
+ "display_url": "twpf.jp/BDFF_LOVE",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 1066,
+ "friends_count": 1799,
+ "listed_count": 6,
+ "created_at": "Fri May 17 12:33:23 +0000 2013",
+ "favourites_count": 1431,
+ "utc_offset": 32400,
+ "time_zone": "Irkutsk",
+ "geo_enabled": true,
+ "verified": false,
+ "statuses_count": 6333,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/505696320380612608/qvaxb_zx_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/505696320380612608/qvaxb_zx_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1435517814/1409401948",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:02 +0000 2014",
+ "id": 505874871130136600,
+ "id_str": "505874871130136576",
+ "text": "闇「リンã¨ä»˜ãåˆã†ã«å½“ãŸã£ã¦æ­³ã®å·®ä»¥å¤–ã«ã‚‚ã„ã‚ã„ã‚å£ãŒã‚ã£ãŸã‚“ã ã‚ˆã€‚æ„›ã—隊ã®å¦¨å®³ã¨ã‹é¢¨ç´€åŽ¨ã®ç”Ÿå¾’会長ã¨ã‹â€¦ã€\n一å·ã€Œãƒªãƒ³ã¡ã‚ƒã‚“ã‚’æ³£ã‹ã›ãŸã‚‰ã‚·ãƒ¡ã‚‹ã‹ã‚“ã­ï¼ã€\n二å·ã€Œãƒªãƒ³ã¡ã‚ƒã‚“ã«ã‚„ã¾ã—ã„事ã—ãŸã‚‰Ã—ã™â€¦ã€\n執行部「ä¸ç´”ãªäº¤éš›ã¯åƒ•ãŒå–ã‚Šç· ã¾ã‚ã†ã˜ã‚ƒãªã„ã‹â€¦ã€\n闇「(消ã•ã‚Œã‚‹ï¼‰ã€",
+ "source": "<a href=\"http://twittbot.net/\" rel=\"nofollow\">twittbot.net</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2386208737,
+ "id_str": "2386208737",
+ "name": "闇未æ¥Bot",
+ "screen_name": "StxRinFbot",
+ "location": "DIVAルーム",
+ "description": "ProjectDIVAã®ãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ãƒ»ã‚¹ãƒˆãƒ¬ãƒ³ã‚¸ãƒ€ãƒ¼ã‚¯Ã—é¡éŸ³ãƒªãƒ³FutureStyleã®è‡ªå·±æº€è¶³éžå…¬å¼Bot マセレン仕様。CPè¦ç´ ã‚ã‚Šã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 7,
+ "friends_count": 2,
+ "listed_count": 0,
+ "created_at": "Thu Mar 13 02:58:09 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 4876,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/443948925351755776/6rmljL5C_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/443948925351755776/6rmljL5C_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2386208737/1396259004",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:02 +0000 2014",
+ "id": 505874870933016600,
+ "id_str": "505874870933016576",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/suitestengoku\" rel=\"nofollow\">絶å“!!スイーツ天国</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2725681663,
+ "id_str": "2725681663",
+ "name": "絶å“!!スイーツ天国",
+ "screen_name": "suitestengoku",
+ "location": "",
+ "description": "美味ã—ãã†ãªã‚¹ã‚¤ãƒ¼ãƒ„ã£ã¦ã€è¦‹ã¦ã‚‹ã ã‘ã§å¹¸ã›ãªæ°—分ã«ãªã‚Œã¾ã™ã­â™ª\r\nãã‚“ãªç´ æ•µãªã‚¹ã‚¤ãƒ¼ãƒ„ã«å‡ºä¼šã„ãŸã„ã§ã™ã€‚\r\n食ã¹ãŸã„ã¨æ€ã£ãŸã‚‰æ˜¯éž RT & フォローãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 401,
+ "friends_count": 1877,
+ "listed_count": 1,
+ "created_at": "Tue Aug 12 07:43:52 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 554,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/499099533507178496/g5dNpArt_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/499099533507178496/g5dNpArt_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2725681663/1407829743",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:02 +0000 2014",
+ "id": 505874870148669440,
+ "id_str": "505874870148669440",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/dengeki_omoro\" rel=\"nofollow\">電車厳ç¦!!ãŠã‚‚ã—ã‚話</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2699667800,
+ "id_str": "2699667800",
+ "name": "電車厳ç¦!!ãŠã‚‚ã—ã‚話w",
+ "screen_name": "dengeki_omoro",
+ "location": "",
+ "description": "日常ã®ã‚ªãƒ¢ã‚·ãƒ­ãã¦ç¬‘ãˆã‚‹å ´é¢ã‚’探ã—ã¾ã™â™ª\r\né¢ç™½ã‹ã£ãŸã‚‰RT & 相互フォローã§ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 461,
+ "friends_count": 1919,
+ "listed_count": 0,
+ "created_at": "Sat Aug 02 02:16:32 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 728,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/495400387961036800/BBMb_hcG_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/495400387961036800/BBMb_hcG_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2699667800/1406947654",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:02 +0000 2014",
+ "id": 505874869339189250,
+ "id_str": "505874869339189249",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/ketueki_face\" rel=\"nofollow\">笑ãˆã‚‹wwランキング2</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2695745652,
+ "id_str": "2695745652",
+ "name": "笑ãˆã‚‹wwランキング",
+ "screen_name": "wara_runk",
+ "location": "",
+ "description": "知ã£ã¦ã‚‹ã¨ä½¿ãˆã‚‹ãƒ©ãƒ³ã‚­ãƒ³ã‚°ã‚’探ãã†ã€‚\r\né¢ç™½ã‹ã£ãŸã‚‰RT & 相互フォローã§ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 314,
+ "friends_count": 1943,
+ "listed_count": 1,
+ "created_at": "Thu Jul 31 13:51:57 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 737,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/494844659856728064/xBQfnm5J_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/494844659856728064/xBQfnm5J_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2695745652/1406815103",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:02 +0000 2014",
+ "id": 505874868533854200,
+ "id_str": "505874868533854209",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/sunikar_daisuki\" rel=\"nofollow\">スニーカー大好ã★図鑑</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2707963890,
+ "id_str": "2707963890",
+ "name": "スニーカー大好ã★図鑑",
+ "screen_name": "sunikar_daisuki",
+ "location": "",
+ "description": "スニーカー好ãを見ã¤ã‘ã¦ä»²é–“ã«ãªã‚ã†â™ª\r\næ°—ã«å…¥ã£ãŸã‚‰ RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 394,
+ "friends_count": 1891,
+ "listed_count": 0,
+ "created_at": "Tue Aug 05 01:54:28 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 642,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/496474952631996416/f0C_u3_u_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/496474952631996416/f0C_u3_u_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2707963890/1407203869",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "zh"
+ },
+ "created_at": "Sun Aug 31 00:29:01 +0000 2014",
+ "id": 505874867997380600,
+ "id_str": "505874867997380608",
+ "text": "\"@BelloTexto: ¿Quieres ser feliz? \n一\"No stalkees\" \n一\"No stalkees\" \n一\"No stalkees\" \n一\"No stalkees\" \n一\"No stalkees\" \n一\"No stalkees\".\"",
+ "source": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2249378935,
+ "id_str": "2249378935",
+ "name": "Maggie Becerril ",
+ "screen_name": "maggdesie",
+ "location": "",
+ "description": "cambiando la vida de las personas.",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 120,
+ "friends_count": 391,
+ "listed_count": 0,
+ "created_at": "Mon Dec 16 21:56:49 +0000 2013",
+ "favourites_count": 314,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 1657,
+ "lang": "es",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/505093371665604608/K0x_LV2y_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/505093371665604608/K0x_LV2y_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2249378935/1409258479",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "BelloTexto",
+ "name": "Indirectas... ✉",
+ "id": 833083404,
+ "id_str": "833083404",
+ "indices": [
+ 1,
+ 12
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "zh"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:01 +0000 2014",
+ "id": 505874867720183800,
+ "id_str": "505874867720183808",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/iseiuragao\" rel=\"nofollow\">ザ・異性ã®è£ã®é¡”</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2719746578,
+ "id_str": "2719746578",
+ "name": "ザ・異性ã®è£ã®é¡”",
+ "screen_name": "iseiuragao",
+ "location": "",
+ "description": "異性ã«ã¤ã„ã¦å°‘ã—å­¦ã¶ã“ã¨ã§ã€å¿…然的ã«ãƒ¢ãƒ†ã‚‹ã‚ˆã†ã«ãªã‚‹ï¼ï¼Ÿã€€ç›¸æ‰‹ã‚’ç†è§£ã™ã‚‹ã“ã¨ã§è¦‹ãˆã¦ãã‚‹ã‚‚ã®ã€Œãã‚Œã¯ãƒ»ãƒ»ãƒ»â—â—ã€ã€€ã„ã„内容ã ã¨æ€ã£ãŸã‚‰ RT & フォローもãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 238,
+ "friends_count": 1922,
+ "listed_count": 0,
+ "created_at": "Sat Aug 09 17:18:43 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 532,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/498157077726900224/tW8q4di__normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/498157077726900224/tW8q4di__normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2719746578/1407604947",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:01 +0000 2014",
+ "id": 505874866910687200,
+ "id_str": "505874866910687233",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/bijyoalbum\" rel=\"nofollow\">超w美女☆アルãƒãƒ </a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2744054334,
+ "id_str": "2744054334",
+ "name": "超w美女☆アルãƒãƒ ",
+ "screen_name": "bijyoalbum",
+ "location": "",
+ "description": "「ãŠãŠï½žã£ï¼ã„ã„ã­ï½žã€ã£ã¦ã€æ€ã‚ãšè¨€ã£ã¦ã—ã¾ã†ã€ç¾Žå¥³ã‚’見ã¤ã‘ã¾ã™â˜† \r\nタイプã ã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 45,
+ "friends_count": 966,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 05:36:48 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 352,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501604413312491520/GP66eKWr_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501604413312491520/GP66eKWr_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744054334/1408426814",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:01 +0000 2014",
+ "id": 505874866105376800,
+ "id_str": "505874866105376769",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/jyoshiuraseitai\" rel=\"nofollow\">ç”·ã«è¦‹ã›ãªã„女å­ã®è£ç”Ÿæ…‹</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2744261238,
+ "id_str": "2744261238",
+ "name": "ç”·ã«è¦‹ã›ãªã„女å­ã®è£ç”Ÿæ…‹",
+ "screen_name": "jyoshiuraseitai",
+ "location": "",
+ "description": "ç”·ã®çŸ¥ã‚‰ãªã„女å­ãªã‚‰ã§ã¯ã®ã‚ã‚‹ã‚る☆ \r\nãã‚“ãªç”Ÿã€…ã—ã„女å­ã®ç”Ÿæ…‹ã‚’ã¤ã¶ã‚„ãã¾ã™ã€‚ \r\nã‚ã‹ã‚‹ï½žã£ã¦äººã¯ RT & フォローã§ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 203,
+ "friends_count": 967,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 08:01:28 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 348,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501641354804346880/Uh1-n1LD_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501641354804346880/Uh1-n1LD_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744261238/1408435630",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:01 +0000 2014",
+ "id": 505874865354584060,
+ "id_str": "505874865354584064",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/soubutu_seitai\" rel=\"nofollow\">é©šãã®å‹•ç‰©ãŸã¡ã®ç”Ÿæ…‹</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2759403146,
+ "id_str": "2759403146",
+ "name": "é©šãã®å‹•ç‰©ãŸã¡ã®ç”Ÿæ…‹",
+ "screen_name": "soubutu_seitai",
+ "location": "",
+ "description": "「ãŠãŠï½žã£ã€ã¨ 言ã‚れるよã†ãªã€å‹•ç‰©ã®ç”Ÿæ…‹ã‚’ツイートã—ã¾ã™â™ª \r\n知ã£ã¦ã„ã‚‹ã¨ã€ã‚ãªãŸã‚‚人気者ã«!? \r\nãŠã‚‚ã—ã‚ã‹ã£ãŸã‚‰ RT & フォローをã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 67,
+ "friends_count": 954,
+ "listed_count": 0,
+ "created_at": "Sat Aug 23 16:39:31 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 219,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503220468128567296/Z8mGDIBS_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503220468128567296/Z8mGDIBS_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2759403146/1408812130",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:01 +0000 2014",
+ "id": 505874864603820000,
+ "id_str": "505874864603820032",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/mote_woman\" rel=\"nofollow\">モテ女å­â˜…ファションã®ç§˜å¯†</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2706659820,
+ "id_str": "2706659820",
+ "name": "モテ女å­â˜…ファションã®ç§˜å¯†",
+ "screen_name": "mote_woman",
+ "location": "",
+ "description": "オシャレã‹ã‚ã„ã„♥モテ度UPã®æ³¨ç›®ã‚¢ã‚¤ãƒ†ãƒ ã‚’見ã¤ã‘ã¾ã™ã€‚\r\næ°—ã«å…¥ã£ãŸã‚‰ RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 217,
+ "friends_count": 1806,
+ "listed_count": 0,
+ "created_at": "Mon Aug 04 14:30:24 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 682,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/496303370936668161/s7xP8rTy_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/496303370936668161/s7xP8rTy_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2706659820/1407163059",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:00 +0000 2014",
+ "id": 505874863874007040,
+ "id_str": "505874863874007040",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/danjyonotigai1\" rel=\"nofollow\">男女ã®é•ã„を解明ã™ã‚‹</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2761896468,
+ "id_str": "2761896468",
+ "name": "男女ã®é•ã„を解明ã™ã‚‹",
+ "screen_name": "danjyonotigai1",
+ "location": "",
+ "description": "æ„外ã¨ç†è§£ã§ãã¦ã„ãªã„男女ãã‚Œãžã‚Œã®äº‹æƒ…。 \r\n「ãˆã£ã€€ãƒžã‚¸ã§!?ã€ã¨é©šãよã†ãªã€ç”·å¥³ã®ç¿’性をã¤ã¶ã‚„ãã¾ã™â™ª ãŸã‚ã«ãªã£ãŸã‚‰ã€æ˜¯éž RT & フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 82,
+ "friends_count": 992,
+ "listed_count": 0,
+ "created_at": "Sun Aug 24 09:47:44 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 237,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503479057380413441/zDLu5Z9o_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503479057380413441/zDLu5Z9o_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2761896468/1408873803",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:00 +0000 2014",
+ "id": 505874862900924400,
+ "id_str": "505874862900924416",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/kamihassou\" rel=\"nofollow\">神レベル★極é™ã®ç™ºæƒ³</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2744950735,
+ "id_str": "2744950735",
+ "name": "神レベル★極é™ã®ç™ºæƒ³",
+ "screen_name": "kamihassou",
+ "location": "",
+ "description": "見ã¦ã„ã‚‹ã ã‘ã§ã€æœ¬æ°—ãŒãƒ“ã‚·ãƒã‚·ä¼ã‚ã£ã¦ãã¾ã™ï¼ \r\n人生ã®ãƒ’ントã«ãªã‚‹ã‚ˆã†ãªã€ãã‚“ãªç©¶æ¥µã®ç™ºæƒ³ã‚’集ã‚ã¦ã„ã¾ã™ã€‚ \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 84,
+ "friends_count": 992,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 13:36:05 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 343,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501725053189226496/xZNOTYz2_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501725053189226496/xZNOTYz2_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744950735/1408455571",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:00 +0000 2014",
+ "id": 505874862397591550,
+ "id_str": "505874862397591552",
+ "text": "@kaoritoxx ãã†ã‚ˆï¼ã‚ãŸã—ã¯ãã†æ€ã†ã‚ˆã†ã«ã—ã¦ãŠã‚‹ã€‚ã„ã¾è·å ´ä¸€ã‚„ã‘ã¨ã‚‹æ°—ãŒã™ã‚‹(°_°)ï¼æº€å–«å¹¸ã›ç„¼ã‘ï¼ï¼wã‚ーã€ãªã‚‹ã»ã©ã­ï¼æ¯Žå›žãã†ã ã‚ˆã­ï¼ãƒ†ã‚£ã‚¢ãƒ©ã¡ã‚ƒã‚“ã¿ã«ã„ã£ã¦ã‚‹ã‚‚ã‚“ã­â™¡äº”月ã¨ä¹æœˆæã‚ã—ã„ã€ã€ã€\nãƒãƒªãƒã‚¿ã‚¨ãƒªã‚¢ã¯ã„ã£ãŸï¼Ÿï¼Ÿ",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": 505838547308277760,
+ "in_reply_to_status_id_str": "505838547308277761",
+ "in_reply_to_user_id": 796000214,
+ "in_reply_to_user_id_str": "796000214",
+ "in_reply_to_screen_name": "kaoritoxx",
+ "user": {
+ "id": 2256249487,
+ "id_str": "2256249487",
+ "name": "ã¯ã‚ã¡ã‚ƒã‚“@海賊åŒç›Ÿä¸­",
+ "screen_name": "onepiece_24",
+ "location": "ã©ãˆã™ãˆã‚ã‰ãŸã‚“ã®åŠ©æ‰‹å…¼ã­å¦¹(願望)",
+ "description": "ONE PIECEæ„›ã—ã™ãŽã¦ä»Šå¹´ï¼’3ã¡ã‚ƒã„(歴14年目)ゾロ様ã«ä¸€é€”ã ã£ãŸã®ã«ãƒ­ãƒ¼ã€ã“ã®ã‚„ã‚ー。ロビンã¡ã‚ƒã‚“ãŒå¹¸ã›ã«ãªã‚Œã°ã„ã„。ルフィã¯ç„¡æ¡ä»¶ã«ã™ã。ゾロビンã€ãƒ­ãƒ¼ãƒ­ãƒ“ã€ãƒ«ãƒ­ãƒ“♡usjã€å£°å„ªã•ã‚“ã€ã‚³ãƒŠãƒ³ã€é€²æ’ƒã€ã‚¯ãƒ¬ã—ã‚“ã€H x Hも好ã♩",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 415,
+ "friends_count": 384,
+ "listed_count": 3,
+ "created_at": "Sat Dec 21 09:37:25 +0000 2013",
+ "favourites_count": 1603,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 9636,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501686340564418561/hMQFN4vD_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501686340564418561/hMQFN4vD_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2256249487/1399987924",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "kaoritoxx",
+ "name": "ã‹ãŠã¡ã‚ƒã‚“",
+ "id": 796000214,
+ "id_str": "796000214",
+ "indices": [
+ 0,
+ 10
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:00 +0000 2014",
+ "id": 505874861973991400,
+ "id_str": "505874861973991424",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/renai_sennin\" rel=\"nofollow\">æ‹æ„›ä»™äºº</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2698885082,
+ "id_str": "2698885082",
+ "name": "æ‹æ„›ä»™äºº",
+ "screen_name": "renai_sennin",
+ "location": "",
+ "description": "豊富ã§ã‚¹ãƒ†ã‚­ãªæ‹æ„›çµŒé¨“ã‚’ã€ã‚·ã‚§ã‚¢ã—ã¾ã—ょã†ã€‚\r\né¢ç™½ã‹ã£ãŸã‚‰RT & 相互フォローã§ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 618,
+ "friends_count": 1847,
+ "listed_count": 1,
+ "created_at": "Fri Aug 01 18:09:38 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 726,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/495272204641132544/GNA18aOg_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/495272204641132544/GNA18aOg_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2698885082/1406917096",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:00 +0000 2014",
+ "id": 505874861881700350,
+ "id_str": "505874861881700353",
+ "text": "@itsukibot_ 一稀ã®ä¿ºã®ã‚½ãƒ¼ã‚»ãƒ¼ã‚¸ã‚’ペロペロã™ã‚‹éŸ³ã¯ãƒ‡ã‚«ã‚¤",
+ "source": "<a href=\"http://jigtwi.jp/?p=1\" rel=\"nofollow\">jigtwi</a>",
+ "truncated": false,
+ "in_reply_to_status_id": 505871017428795400,
+ "in_reply_to_status_id_str": "505871017428795392",
+ "in_reply_to_user_id": 141170845,
+ "in_reply_to_user_id_str": "141170845",
+ "in_reply_to_screen_name": "itsukibot_",
+ "user": {
+ "id": 2184752048,
+ "id_str": "2184752048",
+ "name": "アンドー",
+ "screen_name": "55dakedayo",
+ "location": "",
+ "description": "",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 15,
+ "friends_count": 24,
+ "listed_count": 0,
+ "created_at": "Sat Nov 09 17:42:22 +0000 2013",
+ "favourites_count": 37249,
+ "utc_offset": 32400,
+ "time_zone": "Irkutsk",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 21070,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://abs.twimg.com/sticky/default_profile_images/default_profile_3_normal.png",
+ "profile_image_url_https": "https://abs.twimg.com/sticky/default_profile_images/default_profile_3_normal.png",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": true,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "itsukibot_",
+ "name": "å‰ç”°ä¸€ç¨€",
+ "id": 141170845,
+ "id_str": "141170845",
+ "indices": [
+ 0,
+ 11
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:00 +0000 2014",
+ "id": 505874861185437700,
+ "id_str": "505874861185437697",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/densetunodorama\" rel=\"nofollow\">ã‚ã®ä¼èª¬ã®åドラマ&åå ´é¢</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2706951979,
+ "id_str": "2706951979",
+ "name": "ã‚ã®ä¼èª¬ã®åドラマ&åå ´é¢",
+ "screen_name": "densetunodorama",
+ "location": "",
+ "description": "誰ã«ã§ã‚‚記憶ã«æ®‹ã‚‹ã€ãƒ‰ãƒ©ãƒžã®åå ´é¢ãŒã‚ã‚‹ã¨æ€ã„ã¾ã™ã€‚ãã‚“ãªæ„Ÿå‹•ã®ã‚¹ãƒˆãƒ¼ãƒªãƒ¼ã‚’ã€ã‚‚ã†ä¸€åº¦ã‚ã‹ã¡ã‚ã„ãŸã„ã§ã™ã€‚\r\n「ã“れ知ã£ã¦ã‚‹ï¼ã€ã¨ã‹ã€Œã‚~æ‡ã‹ã—ã„ã€ã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 300,
+ "friends_count": 1886,
+ "listed_count": 0,
+ "created_at": "Mon Aug 04 16:38:25 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 694,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/496335892152209408/fKzb8Nv3_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/496335892152209408/fKzb8Nv3_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2706951979/1407170704",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:29:00 +0000 2014",
+ "id": 505874860447260700,
+ "id_str": "505874860447260672",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/tabetaicake1\" rel=\"nofollow\">マジã§é£Ÿã¹ãŸã„♥ケーキ特集</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2724328646,
+ "id_str": "2724328646",
+ "name": "マジã§é£Ÿã¹ãŸã„♥ケーキ特集",
+ "screen_name": "tabetaicake1",
+ "location": "",
+ "description": "女性ã®ç›®ç·šã‹ã‚‰è¦‹ãŸã€ç¾Žå‘³ã—ãã†ãªã‚±ãƒ¼ã‚­ã‚’探ã—求ã‚ã¦ã„ã¾ã™ã€‚\r\n見ã¦ã‚‹ã ã‘ã§ã€ã‚れもコレも食ã¹ãŸããªã£ã¡ã‚ƒã†â™ª\r\n美味ã—ãã†ã ã¨æ€ã£ãŸã‚‰ã€æ˜¯éž RT & フォローãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 158,
+ "friends_count": 1907,
+ "listed_count": 0,
+ "created_at": "Mon Aug 11 17:15:22 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 493,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/498881289844293632/DAa9No9M_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/498881289844293632/DAa9No9M_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2724328646/1407777704",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:59 +0000 2014",
+ "id": 505874859662925800,
+ "id_str": "505874859662925824",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/adi_mania11\" rel=\"nofollow\">アディダス★マニア</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2704003662,
+ "id_str": "2704003662",
+ "name": "アディダス★マニア",
+ "screen_name": "adi_mania11",
+ "location": "",
+ "description": "素敵ãªã‚¢ãƒ‡ã‚£ãƒ€ã‚¹ã®ã‚¢ã‚¤ãƒ†ãƒ ã‚’見ã¤ã‘ãŸã„ã§ã™â™ª\r\næ°—ã«å…¥ã£ã¦ã‚‚らãˆãŸã‚‰ã‚‰RT & 相互フォロー㧠ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 340,
+ "friends_count": 1851,
+ "listed_count": 0,
+ "created_at": "Sun Aug 03 12:26:37 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 734,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/495911561781727235/06QAMVrR_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/495911561781727235/06QAMVrR_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2704003662/1407069046",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:59 +0000 2014",
+ "id": 505874858920513540,
+ "id_str": "505874858920513537",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/moe_pet1\" rel=\"nofollow\">èŒãˆãƒšãƒƒãƒˆå¤§å¥½ã</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2719061228,
+ "id_str": "2719061228",
+ "name": "èŒãˆãƒšãƒƒãƒˆå¤§å¥½ã",
+ "screen_name": "moe_pet1",
+ "location": "",
+ "description": "ã‹ã‚ã„ã„ペットを見るã®ãŒè¶£å‘³ã§ã™â™¥ãã‚“ãªç§ã¨ä¸€ç·’ã«ã„ã‚„ã•ã‚ŒãŸã„人ã„ã¾ã›ã‚“ã‹ï¼Ÿã‹ã‚ã„ã„ã¨æ€ã£ãŸã‚‰ RT & フォローもãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 289,
+ "friends_count": 1812,
+ "listed_count": 0,
+ "created_at": "Sat Aug 09 10:20:25 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 632,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/498051549537386496/QizThq7N_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/498051549537386496/QizThq7N_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2719061228/1407581287",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:59 +0000 2014",
+ "id": 505874858115219460,
+ "id_str": "505874858115219456",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/renaikyoukasyo\" rel=\"nofollow\">æ‹æ„›ã®æ•™ç§‘書 </a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2744344514,
+ "id_str": "2744344514",
+ "name": "æ‹æ„›ã®æ•™ç§‘書",
+ "screen_name": "renaikyoukasyo",
+ "location": "",
+ "description": "ã‚‚ã£ã¨æ—©ã知ã£ã¨ãã¹ãã ã£ãŸï½žï¼çŸ¥ã£ã¦ã„ã‚Œã°ã‚‚ã£ã¨ä¸Šæ‰‹ãã„ã♪ \r\n今ã™ã役立ã¤æ‹æ„›ã«ã¤ã„ã¦ã®é›‘学やマメ知識をãŠå±Šã‘ã—ã¾ã™ã€‚ \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 124,
+ "friends_count": 955,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 08:32:45 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 346,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501655512018997248/7SznYGWi_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501655512018997248/7SznYGWi_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744344514/1408439001",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:59 +0000 2014",
+ "id": 505874857335074800,
+ "id_str": "505874857335074816",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/omorogakusei\" rel=\"nofollow\">オモロã™ãŽã‚‹â˜…学生ã®æ—¥å¸¸</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2699365116,
+ "id_str": "2699365116",
+ "name": "オモロã™ãŽã‚‹â˜…学生ã®æ—¥å¸¸",
+ "screen_name": "omorogakusei",
+ "location": "",
+ "description": "楽ã—ã™ãŽã‚‹å­¦ç”Ÿã®æ—¥å¸¸ã‚’探ã—ã¦ã„ãã¾ã™ã€‚\r\né¢ç™½ã‹ã£ãŸã‚‰RT & 相互フォローã§ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 289,
+ "friends_count": 1156,
+ "listed_count": 2,
+ "created_at": "Fri Aug 01 23:35:18 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 770,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/495353473886478336/S-4B_RVl_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/495353473886478336/S-4B_RVl_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2699365116/1406936481",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:59 +0000 2014",
+ "id": 505874856605257700,
+ "id_str": "505874856605257728",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/akogareinteria\" rel=\"nofollow\">憧れã®â˜…インテリア図鑑</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2721907602,
+ "id_str": "2721907602",
+ "name": "憧れã®â˜…インテリア図鑑",
+ "screen_name": "akogareinteria",
+ "location": "",
+ "description": "自分ã®ä½ã‚€éƒ¨å±‹ã‚‚ã“ã‚“ãªãµã†ã«ã—ã¦ã¿ãŸã„♪ \r\nãã‚“ãªç´ æ•µãªã‚¤ãƒ³ãƒ†ãƒªã‚¢ã‚’ã€æ—¥ã€…探ã—ã¦ã„ã¾ã™w \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 298,
+ "friends_count": 1925,
+ "listed_count": 0,
+ "created_at": "Sun Aug 10 15:59:13 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 540,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/498499374423343105/Wi_izHvT_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/498499374423343105/Wi_izHvT_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2721907602/1407686543",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:59 +0000 2014",
+ "id": 505874856089378800,
+ "id_str": "505874856089378816",
+ "text": "天冥ã®æ¨™ VI 宿怨 PART1 / å°å· 一水\nhttp://t.co/fXIgRt4ffH\n \n#キンドル #天冥ã®æ¨™VI宿怨PART1",
+ "source": "<a href=\"http://twitter.com/\" rel=\"nofollow\">waromett</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1953404612,
+ "id_str": "1953404612",
+ "name": "ã‚ã‚ã‚ã£ã¨",
+ "screen_name": "waromett",
+ "location": "",
+ "description": "ãŸã®ã—ã„ã¤ã„ーã¨ã—ょã†ã‹ã„",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 16980,
+ "friends_count": 16983,
+ "listed_count": 18,
+ "created_at": "Fri Oct 11 05:49:57 +0000 2013",
+ "favourites_count": 3833,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 98655,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "352726",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme5/bg.gif",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme5/bg.gif",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/378800000578908101/14c4744c7aa34b1f8bbd942b78e59385_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000578908101/14c4744c7aa34b1f8bbd942b78e59385_normal.jpeg",
+ "profile_link_color": "D02B55",
+ "profile_sidebar_border_color": "829D5E",
+ "profile_sidebar_fill_color": "99CC33",
+ "profile_text_color": "3E4415",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [
+ {
+ "text": "キンドル",
+ "indices": [
+ 50,
+ 55
+ ]
+ },
+ {
+ "text": "天冥ã®æ¨™VI宿怨PART1",
+ "indices": [
+ 56,
+ 70
+ ]
+ }
+ ],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/fXIgRt4ffH",
+ "expanded_url": "http://j.mp/1kHBOym",
+ "display_url": "j.mp/1kHBOym",
+ "indices": [
+ 25,
+ 47
+ ]
+ }
+ ],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "zh"
+ },
+ "created_at": "Sun Aug 31 00:28:58 +0000 2014",
+ "id": 505874855770599400,
+ "id_str": "505874855770599425",
+ "text": "å››å·ç›†åœ°æ±Ÿæ·®ç­‰åœ°å°†æœ‰å¼ºé™é›¨ 开学日多地将有雨:   中新网8月31日电 æ®ä¸­å¤®æ°”象å°æ¶ˆæ¯ï¼Œæ±Ÿæ·®ä¸œéƒ¨ã€å››å·ç›†åœ°ä¸œåŒ—部等地今天(31æ—¥)åˆå°†è¿Žæ¥ä¸€åœºæš´é›¨æˆ–大暴雨天气。明天9月1日,是中å°å­¦ç”Ÿå¼€å­¦çš„æ—¥å­ã€‚预计明天,内蒙å¤ä¸­éƒ¨ã€... http://t.co/RNdqIHmTby",
+ "source": "<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 402427654,
+ "id_str": "402427654",
+ "name": "中国新闻",
+ "screen_name": "zhongwenxinwen",
+ "location": "",
+ "description": "中国的新闻,世界的新闻。",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 2429,
+ "friends_count": 15,
+ "listed_count": 29,
+ "created_at": "Tue Nov 01 01:56:43 +0000 2011",
+ "favourites_count": 0,
+ "utc_offset": -28800,
+ "time_zone": "Alaska",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 84564,
+ "lang": "zh-cn",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "709397",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme6/bg.gif",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme6/bg.gif",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/2700523149/5597e347b2eb880425faef54287995f2_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/2700523149/5597e347b2eb880425faef54287995f2_normal.jpeg",
+ "profile_link_color": "FF3300",
+ "profile_sidebar_border_color": "86A4A6",
+ "profile_sidebar_fill_color": "A0C5C7",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/RNdqIHmTby",
+ "expanded_url": "http://bit.ly/1tOdNsI",
+ "display_url": "bit.ly/1tOdNsI",
+ "indices": [
+ 114,
+ 136
+ ]
+ }
+ ],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "zh"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:58 +0000 2014",
+ "id": 505874854877200400,
+ "id_str": "505874854877200384",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/LDH_daisuki1\" rel=\"nofollow\">LDH ★大好ãå¿œæ´å›£</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2700961603,
+ "id_str": "2700961603",
+ "name": "LDH ★大好ãå¿œæ´å›£",
+ "screen_name": "LDH_daisuki1",
+ "location": "",
+ "description": "LDHファンã¯ã€å…¨å“¡ä»²é–“ã§ã™â™ª\r\né¢ç™½ã‹ã£ãŸã‚‰RT & 相互フォローã§ã¿ãªã•ã‚“ã€ãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 458,
+ "friends_count": 1895,
+ "listed_count": 0,
+ "created_at": "Sat Aug 02 14:23:46 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 735,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/495578007298252800/FOZflgYu_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/495578007298252800/FOZflgYu_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2700961603/1406989928",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:58 +0000 2014",
+ "id": 505874854147407900,
+ "id_str": "505874854147407872",
+ "text": "RT @shiawaseomamori: 一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®â€¦",
+ "source": "<a href=\"https://twitter.com/anime_toshiden1\" rel=\"nofollow\">マジ!?怖ã„アニメ都市ä¼èª¬</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2719489172,
+ "id_str": "2719489172",
+ "name": "マジ!?怖ã„アニメ都市ä¼èª¬",
+ "screen_name": "anime_toshiden1",
+ "location": "",
+ "description": "ã‚ãªãŸã®çŸ¥ã‚‰ãªã„ã€æ€–ã™ãŽã‚‹ã‚¢ãƒ‹ãƒ¡ã®éƒ½å¸‚ä¼èª¬ã‚’集ã‚ã¦ã„ã¾ã™ã€‚\r\n「ãˆï½žçŸ¥ã‚‰ãªã‹ã£ãŸã‚ˆww]ã€ã£ã¦äººã¯ RT & フォローãŠé¡˜ã„ã—ã¾ã™â™ª",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 377,
+ "friends_count": 1911,
+ "listed_count": 1,
+ "created_at": "Sat Aug 09 14:41:15 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 536,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/498118027322208258/h7XOTTSi_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/498118027322208258/h7XOTTSi_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2719489172/1407595513",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:06 +0000 2014",
+ "id": 505871615125491700,
+ "id_str": "505871615125491712",
+ "text": "一ã«æ­¢ã¾ã‚‹ã¨æ›¸ã„ã¦ã€æ­£ã—ã„ã¨ã„ã†æ„味ã ãªã‚“ã¦ã€ã“ã®å¹´ã«ãªã‚‹ã¾ã§çŸ¥ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ 人ã¯ç”Ÿãã¦ã„ã‚‹ã¨ã€å‰ã¸å‰ã¸ã¨ã„ã†æ°—æŒã¡ã°ã‹ã‚Šæ€¥ã„ã¦ã€ã©ã‚“ã©ã‚“大切ãªã‚‚ã®ã‚’ç½®ã去りã«ã—ã¦ã„ãã‚‚ã®ã§ã—ょã†ã€‚本当ã«æ­£ã—ã„ã“ã¨ã¨ã„ã†ã®ã¯ã€ä¸€ç•ªåˆã‚ã®å ´æ‰€ã«ã‚ã‚‹ã®ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 by神様ã®ã‚«ãƒ«ãƒ†ã€å¤å·è‰ä»‹",
+ "source": "<a href=\"https://twitter.com/shiawaseomamori\" rel=\"nofollow\">幸ã›ã®â˜†ãŠå®ˆã‚Š</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "screen_name": "shiawaseomamori",
+ "location": "",
+ "description": "自分ãŒå¹¸ã›ã ã¨å‘¨ã‚Šã‚‚幸ã›ã«ã§ãã‚‹ï¼ \r\nãã‚“ãªäººç”Ÿã‚’精一æ¯ç”Ÿãã‚‹ãŸã‚ã«å¿…è¦ãªè¨€è‘‰ã‚’ãŠå±Šã‘ã—ã¾ã™â™ª \r\nã„ã„ãªã¨æ€ã£ãŸã‚‰ RT & 相互フォローã§ã€ãŠé¡˜ã„ã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 213,
+ "friends_count": 991,
+ "listed_count": 0,
+ "created_at": "Tue Aug 19 14:45:19 +0000 2014",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 349,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 58,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "shiawaseomamori",
+ "name": "幸ã›ã®â˜†ãŠå®ˆã‚Š",
+ "id": 2745121514,
+ "id_str": "2745121514",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:58 +0000 2014",
+ "id": 505874854134820860,
+ "id_str": "505874854134820864",
+ "text": "@vesperia1985 ãŠã¯ã‚ˆãƒ¼ï¼\n今日ã¾ã§ãªã®ã§ã™ã‚ˆâ€¦ï¼ï¼æ˜Žæ—¥ä¸€ç”Ÿæ¥ãªãã¦ã„ã„",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": 505868030329364500,
+ "in_reply_to_status_id_str": "505868030329364480",
+ "in_reply_to_user_id": 2286548834,
+ "in_reply_to_user_id_str": "2286548834",
+ "in_reply_to_screen_name": "vesperia1985",
+ "user": {
+ "id": 2389045190,
+ "id_str": "2389045190",
+ "name": "ã‚Šã„ã“",
+ "screen_name": "riiko_dq10",
+ "location": "",
+ "description": "サマーエルフã§ã™ã€ã‚Šã„ã“ã§ã™ã€‚ãˆã‚‹ãŠãんラブã§ã™ï¼éšæ™‚ãµã‚Œã¼ã—ゅ〜〜(ã£Ë˜Ï‰Ë˜c )*日常ã®ã©ã†ã§ã‚‚ã„ã„ã“ã¨ã‚‚å‘Ÿã„ã¦ã¾ã™ãŒã‚ˆã‚ã—ãã­ã€œ",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 67,
+ "friends_count": 69,
+ "listed_count": 0,
+ "created_at": "Fri Mar 14 13:02:27 +0000 2014",
+ "favourites_count": 120,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 324,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/503906346815610881/BfSrCoBr_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/503906346815610881/BfSrCoBr_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2389045190/1409232058",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "vesperia1985",
+ "name": "ユーリ",
+ "id": 2286548834,
+ "id_str": "2286548834",
+ "indices": [
+ 0,
+ 13
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:58 +0000 2014",
+ "id": 505874853778685950,
+ "id_str": "505874853778685952",
+ "text": "ã€æ˜ ç”»ãƒ‘ンフレット】 永é ã®ï¼ (永é ã®ã‚¼ãƒ­ï¼‰ã€€ç›£ç£ã€€å±±å´Žè²´ã€€ã‚­ãƒ£ã‚¹ãƒˆã€€å²¡ç”°å‡†ä¸€ã€ä¸‰æµ¦æ˜¥é¦¬ã€äº•ä¸ŠçœŸå¤®æ±å®(2)11点ã®æ–°å“ï¼ä¸­å¤å“を見る: ï¿¥ 500より\n(ã“ã®å•†å“ã®ç¾åœ¨ã®ãƒ©ãƒ³ã‚¯ã«é–¢ã™ã‚‹æ­£å¼ãªæƒ…å ±ã«ã¤ã„ã¦ã¯ã€ã‚¢ãƒ¼ãƒˆãƒ•ãƒ¬ãƒ¼ãƒ ... http://t.co/4hbyB1rbQ7",
+ "source": "<a href=\"http://ifttt.com\" rel=\"nofollow\">IFTTT</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1319883571,
+ "id_str": "1319883571",
+ "name": "森林木工家具製作所",
+ "screen_name": "Furniturewood",
+ "location": "沖縄",
+ "description": "家具(ã‹ãã€Furniture)ã¯ã€å®¶è²¡é“å…·ã®ã†ã¡å®¶ã®ä¸­ã«æ®ãˆç½®ã„ã¦åˆ©ç”¨ã™ã‚‹æ¯”較的大型ã®é“å…·é¡žã€ã¾ãŸã¯å…ƒã€…家ã«ä½œã‚Šä»˜ã‘られã¦ã„る比較的大型ã®é“å…·é¡žã‚’ã•ã™ã€‚ãªãŠã€æ—¥æœ¬ã®å»ºç¯‰åŸºæº–法上ã¯ã€ä½œã‚Šä»˜ã‘家具ã¯ã€å»ºç¯‰ç¢ºèªåŠã³å®Œäº†æ¤œæŸ»ã®å¯¾è±¡ã¨ãªã‚‹ãŒã€å¾Œã‹ã‚‰ç½®ã‹ã‚Œã‚‹ã‚‚ã®ã«ã¤ã„ã¦ã¯å¯¾è±¡å¤–ã§ã‚る。",
+ "url": "http://t.co/V4oyL0xtZk",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/V4oyL0xtZk",
+ "expanded_url": "http://astore.amazon.co.jp/furniturewood-22",
+ "display_url": "astore.amazon.co.jp/furniturewood-…",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 677,
+ "friends_count": 743,
+ "listed_count": 1,
+ "created_at": "Mon Apr 01 07:55:14 +0000 2013",
+ "favourites_count": 0,
+ "utc_offset": 32400,
+ "time_zone": "Irkutsk",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 17210,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/3460466135/c67d9df9b760787b9ed284fe80b1dd31_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/3460466135/c67d9df9b760787b9ed284fe80b1dd31_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1319883571/1364804982",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/4hbyB1rbQ7",
+ "expanded_url": "http://ift.tt/1kT55bk",
+ "display_url": "ift.tt/1kT55bk",
+ "indices": [
+ 116,
+ 138
+ ]
+ }
+ ],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:58 +0000 2014",
+ "id": 505874852754907140,
+ "id_str": "505874852754907136",
+ "text": "RT @siranuga_hotoke: ゴキブリã¯ä¸€ä¸–帯ã«å¹³å‡ã—ã¦480匹ã„る。",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 413944345,
+ "id_str": "413944345",
+ "name": "泥酔イナãƒã‚¦ã‚¢ãƒ¼",
+ "screen_name": "Natade_co_co_21",
+ "location": "",
+ "description": "å›ã®çž³ã«ã†ã¤ã‚‹åƒ•ã«ä¹¾æ¯ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 298,
+ "friends_count": 300,
+ "listed_count": 4,
+ "created_at": "Wed Nov 16 12:52:46 +0000 2011",
+ "favourites_count": 3125,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 12237,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "FFF04D",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/378800000115928444/9bf5fa13385cc80bfeb097e51af9862a.jpeg",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/378800000115928444/9bf5fa13385cc80bfeb097e51af9862a.jpeg",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/500849752351600640/lMQqIzYj_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/500849752351600640/lMQqIzYj_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/413944345/1403511193",
+ "profile_link_color": "0099CC",
+ "profile_sidebar_border_color": "000000",
+ "profile_sidebar_fill_color": "F6FFD1",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sat Aug 30 23:24:23 +0000 2014",
+ "id": 505858599411666940,
+ "id_str": "505858599411666944",
+ "text": "ゴキブリã¯ä¸€ä¸–帯ã«å¹³å‡ã—ã¦480匹ã„る。",
+ "source": "<a href=\"http://twittbot.net/\" rel=\"nofollow\">twittbot.net</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2243896200,
+ "id_str": "2243896200",
+ "name": "知らã¬ãŒä»bot",
+ "screen_name": "siranuga_hotoke",
+ "location": "奈良・京都辺り",
+ "description": "知らã¬ãŒä»ãªæƒ…報をãŠä¼ãˆã—ã¾ã™ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 3288,
+ "friends_count": 3482,
+ "listed_count": 7,
+ "created_at": "Fri Dec 13 13:16:35 +0000 2013",
+ "favourites_count": 0,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 1570,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/378800000866399372/ypy5NnPe_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000866399372/ypy5NnPe_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/2243896200/1386997755",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 1,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ "retweet_count": 1,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [],
+ "user_mentions": [
+ {
+ "screen_name": "siranuga_hotoke",
+ "name": "知らã¬ãŒä»bot",
+ "id": 2243896200,
+ "id_str": "2243896200",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:58 +0000 2014",
+ "id": 505874852603908100,
+ "id_str": "505874852603908096",
+ "text": "RT @UARROW_Y: よã†ã‹ã„体æ“第一を踊る国見英 http://t.co/SXoYWH98as",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 2463035136,
+ "id_str": "2463035136",
+ "name": "ã‚„",
+ "screen_name": "yae45",
+ "location": "",
+ "description": "ãã‚‚ã¡ã‚ã‚‹ã„ã“ã¨ã¤ã¶ã‚„ã用",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 4,
+ "friends_count": 30,
+ "listed_count": 0,
+ "created_at": "Fri Apr 25 10:49:20 +0000 2014",
+ "favourites_count": 827,
+ "utc_offset": 32400,
+ "time_zone": "Irkutsk",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 390,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/505345820137234433/csFeRxPm_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/505345820137234433/csFeRxPm_normal.jpeg",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:16:45 +0000 2014",
+ "id": 505871779949051900,
+ "id_str": "505871779949051904",
+ "text": "よã†ã‹ã„体æ“第一を踊る国見英 http://t.co/SXoYWH98as",
+ "source": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1261662588,
+ "id_str": "1261662588",
+ "name": "ゆã†çŸ¢",
+ "screen_name": "UARROW_Y",
+ "location": "ã¤ãり出ãã†å›½å½±ã®æ³¢ 広ã’よã†å›½å½±ã®è¼ª",
+ "description": "HQ!! æˆäººæ¸ˆè…女å­ã€‚日常ツイート多ã„ã§ã™ã€‚赤葦京治夢豚クソツイå«ã¿ã¾ã™æ³¨æ„。フォローをãŠè€ƒãˆã®éš›ã¯ãƒ—ロフã”一読ãŠé¡˜ã„致ã—ã¾ã™ã€‚FRBãŠæ°—軽ã«",
+ "url": "http://t.co/LFX2XOzb0l",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/LFX2XOzb0l",
+ "expanded_url": "http://twpf.jp/UARROW_Y",
+ "display_url": "twpf.jp/UARROW_Y",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 265,
+ "friends_count": 124,
+ "listed_count": 12,
+ "created_at": "Tue Mar 12 10:42:17 +0000 2013",
+ "favourites_count": 6762,
+ "utc_offset": 32400,
+ "time_zone": "Tokyo",
+ "geo_enabled": true,
+ "verified": false,
+ "statuses_count": 55946,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/502095104618663937/IzuPYx3E_normal.png",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/502095104618663937/IzuPYx3E_normal.png",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1261662588/1408618604",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 29,
+ "favorite_count": 54,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/SXoYWH98as",
+ "expanded_url": "http://twitter.com/UARROW_Y/status/505871779949051904/photo/1",
+ "display_url": "pic.twitter.com/SXoYWH98as",
+ "indices": [
+ 15,
+ 37
+ ]
+ }
+ ],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ "retweet_count": 29,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/SXoYWH98as",
+ "expanded_url": "http://twitter.com/UARROW_Y/status/505871779949051904/photo/1",
+ "display_url": "pic.twitter.com/SXoYWH98as",
+ "indices": [
+ 29,
+ 51
+ ]
+ }
+ ],
+ "user_mentions": [
+ {
+ "screen_name": "UARROW_Y",
+ "name": "ゆã†çŸ¢",
+ "id": 1261662588,
+ "id_str": "1261662588",
+ "indices": [
+ 3,
+ 12
+ ]
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "zh"
+ },
+ "created_at": "Sun Aug 31 00:28:57 +0000 2014",
+ "id": 505874848900341760,
+ "id_str": "505874848900341760",
+ "text": "RT @fightcensorship: æŽå…‹å¼·ç¸½ç†çš„臉綠了ï¼åœ¨å‰æ—¥å—京é’奧會閉幕å¼ï¼Œè§€çœ¾å¸­ä¸Šä¸€å貪玩韓國少年é‹å‹•å“¡ï¼Œç«Ÿæ–—膽用激光筆射å‘中國總ç†æŽå…‹å¼·çš„臉。http://t.co/HLX9mHcQwe http://t.co/fVVOSML5s8",
+ "source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 889332218,
+ "id_str": "889332218",
+ "name": "民權åˆæ­¥",
+ "screen_name": "JoeyYoungkm",
+ "location": "km/cn",
+ "description": "ç»åŽ†äº†æ€Žæ ·çš„曲折æ‰ä»Žè¿½æ±‚“一致通过â€å‘展到今天人们接å—“过åŠæ•°é€šè¿‡â€ï¼Œæ­£æ˜¯äººä»¬è®¤è¯†åˆ°å¯¹â€œä¸€è‡´â€ç”šè‡³æ˜¯â€œåŸºæœ¬ä¸€è‡´â€çš„追求本身就会å˜æˆä¸€ç§ç‹¬è£ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 313,
+ "friends_count": 46,
+ "listed_count": 0,
+ "created_at": "Thu Oct 18 17:21:17 +0000 2012",
+ "favourites_count": 24,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 15707,
+ "lang": "en",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "C0DEED",
+ "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/378800000563062033/a7e8274752ce36a6cd5bad971ec7d416_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000563062033/a7e8274752ce36a6cd5bad971ec7d416_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/889332218/1388896916",
+ "profile_link_color": "0084B4",
+ "profile_sidebar_border_color": "C0DEED",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": true,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweeted_status": {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "zh"
+ },
+ "created_at": "Sat Aug 30 23:56:27 +0000 2014",
+ "id": 505866670356070400,
+ "id_str": "505866670356070401",
+ "text": "æŽå…‹å¼·ç¸½ç†çš„臉綠了ï¼åœ¨å‰æ—¥å—京é’奧會閉幕å¼ï¼Œè§€çœ¾å¸­ä¸Šä¸€å貪玩韓國少年é‹å‹•å“¡ï¼Œç«Ÿæ–—膽用激光筆射å‘中國總ç†æŽå…‹å¼·çš„臉。http://t.co/HLX9mHcQwe http://t.co/fVVOSML5s8",
+ "source": "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 67661086,
+ "id_str": "67661086",
+ "name": "※范强※法特姗瑟希蒲※",
+ "screen_name": "fightcensorship",
+ "location": "Middle of Nowhere",
+ "description": "被人指责“å°å»ºâ€ã€â€œè½åŽâ€ã€â€œä¿å®ˆâ€çš„代表,当代红å«å…µæ”»å‡»å¯¹è±¡ã€‚致力于言论自由,人æƒï¼› 倡导资讯公开,å对网络å°é”。既ä¸æ˜¯ç²¾è‹±åˆ†å­ï¼Œä¹Ÿä¸æ˜¯æ„è§é¢†è¢–,本推言论ä¸ä»£è¡¨ä»»ä½•å›½å®¶ã€å…šæ´¾å’Œç»„织,也ä¸æ ‡æ¦œä¼Ÿå¤§ã€å…‰è£å’Œæ­£ç¡®ã€‚",
+ "url": null,
+ "entities": {
+ "description": {
+ "urls": []
+ }
+ },
+ "protected": false,
+ "followers_count": 7143,
+ "friends_count": 779,
+ "listed_count": 94,
+ "created_at": "Fri Aug 21 17:16:22 +0000 2009",
+ "favourites_count": 364,
+ "utc_offset": 28800,
+ "time_zone": "Singapore",
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 16751,
+ "lang": "en",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "FFFFFF",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/611138516/toeccqnahbpmr0sw9ybv.jpeg",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/611138516/toeccqnahbpmr0sw9ybv.jpeg",
+ "profile_background_tile": true,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/3253137427/3524557d21ef2c04871e985d4d136bdb_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/3253137427/3524557d21ef2c04871e985d4d136bdb_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/67661086/1385608347",
+ "profile_link_color": "ED1313",
+ "profile_sidebar_border_color": "FFFFFF",
+ "profile_sidebar_fill_color": "E0FF92",
+ "profile_text_color": "000000",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 4,
+ "favorite_count": 2,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/HLX9mHcQwe",
+ "expanded_url": "http://is.gd/H3OgTO",
+ "display_url": "is.gd/H3OgTO",
+ "indices": [
+ 57,
+ 79
+ ]
+ }
+ ],
+ "user_mentions": [],
+ "media": [
+ {
+ "id": 505866668485386240,
+ "id_str": "505866668485386241",
+ "indices": [
+ 80,
+ 102
+ ],
+ "media_url": "http://pbs.twimg.com/media/BwUzDgbIIAEgvhD.jpg",
+ "media_url_https": "https://pbs.twimg.com/media/BwUzDgbIIAEgvhD.jpg",
+ "url": "http://t.co/fVVOSML5s8",
+ "display_url": "pic.twitter.com/fVVOSML5s8",
+ "expanded_url": "http://twitter.com/fightcensorship/status/505866670356070401/photo/1",
+ "type": "photo",
+ "sizes": {
+ "large": {
+ "w": 640,
+ "h": 554,
+ "resize": "fit"
+ },
+ "medium": {
+ "w": 600,
+ "h": 519,
+ "resize": "fit"
+ },
+ "thumb": {
+ "w": 150,
+ "h": 150,
+ "resize": "crop"
+ },
+ "small": {
+ "w": 340,
+ "h": 294,
+ "resize": "fit"
+ }
+ }
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "zh"
+ },
+ "retweet_count": 4,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/HLX9mHcQwe",
+ "expanded_url": "http://is.gd/H3OgTO",
+ "display_url": "is.gd/H3OgTO",
+ "indices": [
+ 78,
+ 100
+ ]
+ }
+ ],
+ "user_mentions": [
+ {
+ "screen_name": "fightcensorship",
+ "name": "※范强※法特姗瑟希蒲※",
+ "id": 67661086,
+ "id_str": "67661086",
+ "indices": [
+ 3,
+ 19
+ ]
+ }
+ ],
+ "media": [
+ {
+ "id": 505866668485386240,
+ "id_str": "505866668485386241",
+ "indices": [
+ 101,
+ 123
+ ],
+ "media_url": "http://pbs.twimg.com/media/BwUzDgbIIAEgvhD.jpg",
+ "media_url_https": "https://pbs.twimg.com/media/BwUzDgbIIAEgvhD.jpg",
+ "url": "http://t.co/fVVOSML5s8",
+ "display_url": "pic.twitter.com/fVVOSML5s8",
+ "expanded_url": "http://twitter.com/fightcensorship/status/505866670356070401/photo/1",
+ "type": "photo",
+ "sizes": {
+ "large": {
+ "w": 640,
+ "h": 554,
+ "resize": "fit"
+ },
+ "medium": {
+ "w": 600,
+ "h": 519,
+ "resize": "fit"
+ },
+ "thumb": {
+ "w": 150,
+ "h": 150,
+ "resize": "crop"
+ },
+ "small": {
+ "w": 340,
+ "h": 294,
+ "resize": "fit"
+ }
+ },
+ "source_status_id": 505866670356070400,
+ "source_status_id_str": "505866670356070401"
+ }
+ ]
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "zh"
+ },
+ {
+ "metadata": {
+ "result_type": "recent",
+ "iso_language_code": "ja"
+ },
+ "created_at": "Sun Aug 31 00:28:56 +0000 2014",
+ "id": 505874847260352500,
+ "id_str": "505874847260352513",
+ "text": "ã€ãƒžã‚¤ãƒªã‚¹ãƒˆã€‘ã€å½©ã‚Šã‚Šã‚】妖怪体æ“第一 踊ã£ã¦ã¿ãŸã€å転】 http://t.co/PjL9if8OZC #sm24357625",
+ "source": "<a href=\"http://www.nicovideo.jp/\" rel=\"nofollow\">ニコニコ動画</a>",
+ "truncated": false,
+ "in_reply_to_status_id": null,
+ "in_reply_to_status_id_str": null,
+ "in_reply_to_user_id": null,
+ "in_reply_to_user_id_str": null,
+ "in_reply_to_screen_name": null,
+ "user": {
+ "id": 1609789375,
+ "id_str": "1609789375",
+ "name": "食ã„ã—ã‚“åŠå‰ã¡ã‚ƒã‚“",
+ "screen_name": "2no38mae",
+ "location": "ニノã¨äºŒæ¬¡å…ƒã®é–“",
+ "description": "ニコ動ã§è¸Šã‚Šæ‰‹ã‚„ã£ã¦ã¾ã™!!å¿œæ´æœ¬å½“ã«å¬‰ã—ã„ã§ã™ã‚ã‚ŠãŒã¨ã†ã”ã–ã„ã¾ã™!! ã½ã£ã¡ã‚ƒã‚Šã ã‘ã©å‰å‘ãã«é ‘張るè…女å­ã§ã™ã€‚åµã¨å¼±è™«ãƒšãƒ€ãƒ«ãŒå¤§å¥½ãï¼ã€ãŠè¿”事】りã·(基本ã¯)â€â—‹â€ã€€DM (åŒæ¥­è€…様を除ã„ã¦)â€Ã—â€ã€€å‹•ç”»ã®è»¢è¼‰ã¯çµ¶å¯¾ã«ã‚„ã‚ã¦ãã ã•ã„。 ブログ→http://t.co/8E91tqoeKX  ",
+ "url": "http://t.co/ulD2e9mcwb",
+ "entities": {
+ "url": {
+ "urls": [
+ {
+ "url": "http://t.co/ulD2e9mcwb",
+ "expanded_url": "http://www.nicovideo.jp/mylist/37917495",
+ "display_url": "nicovideo.jp/mylist/37917495",
+ "indices": [
+ 0,
+ 22
+ ]
+ }
+ ]
+ },
+ "description": {
+ "urls": [
+ {
+ "url": "http://t.co/8E91tqoeKX",
+ "expanded_url": "http://ameblo.jp/2no38mae/",
+ "display_url": "ameblo.jp/2no38mae/",
+ "indices": [
+ 125,
+ 147
+ ]
+ }
+ ]
+ }
+ },
+ "protected": false,
+ "followers_count": 560,
+ "friends_count": 875,
+ "listed_count": 11,
+ "created_at": "Sun Jul 21 05:09:43 +0000 2013",
+ "favourites_count": 323,
+ "utc_offset": null,
+ "time_zone": null,
+ "geo_enabled": false,
+ "verified": false,
+ "statuses_count": 3759,
+ "lang": "ja",
+ "contributors_enabled": false,
+ "is_translator": false,
+ "is_translation_enabled": false,
+ "profile_background_color": "F2C6E4",
+ "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/378800000029400927/114b242f5d838ec7cb098ea5db6df413.jpeg",
+ "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/378800000029400927/114b242f5d838ec7cb098ea5db6df413.jpeg",
+ "profile_background_tile": false,
+ "profile_image_url": "http://pbs.twimg.com/profile_images/487853237723095041/LMBMGvOc_normal.jpeg",
+ "profile_image_url_https": "https://pbs.twimg.com/profile_images/487853237723095041/LMBMGvOc_normal.jpeg",
+ "profile_banner_url": "https://pbs.twimg.com/profile_banners/1609789375/1375752225",
+ "profile_link_color": "FF9EDD",
+ "profile_sidebar_border_color": "FFFFFF",
+ "profile_sidebar_fill_color": "DDEEF6",
+ "profile_text_color": "333333",
+ "profile_use_background_image": true,
+ "default_profile": false,
+ "default_profile_image": false,
+ "following": false,
+ "follow_request_sent": false,
+ "notifications": false
+ },
+ "geo": null,
+ "coordinates": null,
+ "place": null,
+ "contributors": null,
+ "retweet_count": 0,
+ "favorite_count": 0,
+ "entities": {
+ "hashtags": [
+ {
+ "text": "sm24357625",
+ "indices": [
+ 53,
+ 64
+ ]
+ }
+ ],
+ "symbols": [],
+ "urls": [
+ {
+ "url": "http://t.co/PjL9if8OZC",
+ "expanded_url": "http://nico.ms/sm24357625",
+ "display_url": "nico.ms/sm24357625",
+ "indices": [
+ 30,
+ 52
+ ]
+ }
+ ],
+ "user_mentions": []
+ },
+ "favorited": false,
+ "retweeted": false,
+ "possibly_sensitive": false,
+ "lang": "ja"
+ }
+ ],
+ "search_metadata": {
+ "completed_in": 0.087,
+ "max_id": 505874924095815700,
+ "max_id_str": "505874924095815681",
+ "next_results": "?max_id=505874847260352512&q=%E4%B8%80&count=100&include_entities=1",
+ "query": "%E4%B8%80",
+ "refresh_url": "?since_id=505874924095815681&q=%E4%B8%80&include_entities=1",
+ "count": 100,
+ "since_id": 0,
+ "since_id_str": "0"
+ }
+} \ No newline at end of file
diff --git a/bom/build.gradle b/bom/build.gradle
new file mode 100644
index 00000000..12e2f6b7
--- /dev/null
+++ b/bom/build.gradle
@@ -0,0 +1,41 @@
+plugins {
+ id 'java-platform'
+}
+
+def name = project.name
+
+dependencies {
+ constraints {
+ rootProject.subprojects.each {
+ if (it.name == name) return
+ if (!it.plugins.hasPlugin('maven-publish')) return
+ evaluationDependsOn(it.path)
+ it.publishing.publications.all {
+ if (it.artifactId.endsWith("-kotlinMultiplatform")) return
+ if (it.artifactId.endsWith("-metadata")) return
+ // Skip platform artifacts (like *-linuxx64, *-macosx64)
+ // It leads to inconsistent bom when publishing from different platforms
+ // (e.g. on linux it will include only linuxx64 artifacts and no macosx64)
+ // It shouldn't be a problem as usually consumers need to use generic *-native artifact
+ // Gradle will choose correct variant by using metadata attributes
+ if (it.artifacts.any { it.extension == 'klib' }) return
+ api("${it.groupId}:${it.artifactId}:${it.version}")
+ }
+ }
+ }
+}
+
+publishing {
+ publications {
+ mavenBom(MavenPublication) {
+ from components.javaPlatform
+ }
+ // Disable metadata publication, no need to
+ it.each { pub ->
+ pub.moduleDescriptorGenerator = null
+ tasks.matching { it.name == "generateMetadataFileFor${pub.name.capitalize()}Publication" }.all {
+ onlyIf { false }
+ }
+ }
+ }
+}
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..69aa68dd
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+buildscript {
+ if (project.hasProperty("bootstrap")) {
+ ext.kotlin_version = property('kotlin.version.snapshot')
+ ext["kotlin.native.home"] = System.getenv("KONAN_LOCAL_DIST")
+ } else {
+ ext.kotlin_version = property('kotlin.version')
+ }
+ if (project.hasProperty("library.version")) {
+ ext.overriden_version = property('library.version')
+ }
+ ext.experimentalsEnabled = ["-progressive", "-opt-in=kotlin.Experimental",
+ "-opt-in=kotlin.ExperimentalMultiplatform",
+ "-opt-in=kotlinx.serialization.InternalSerializationApi"
+ ]
+
+ ext.experimentalsInTestEnabled = ["-progressive", "-opt-in=kotlin.Experimental",
+ "-opt-in=kotlin.ExperimentalMultiplatform",
+ "-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
+ "-opt-in=kotlinx.serialization.InternalSerializationApi",
+ "-opt-in=kotlin.ExperimentalUnsignedTypes"
+ ]
+ ext.koverEnabled = property('kover.enabled') ?: true
+
+ /*
+ * This property group is used to build kotlinx.serialization against Kotlin compiler snapshot.
+ * When build_snapshot_train is set to true, kotlin_version property is overridden with kotlin_snapshot_version.
+ * DO NOT change the name of these properties without adapting kotlinx.train build chain.
+ */
+ def prop = rootProject.properties['build_snapshot_train']
+ ext.build_snapshot_train = prop != null && prop != ""
+ if (build_snapshot_train) {
+ ext.kotlin_version = rootProject.properties['kotlin_snapshot_version']
+ if (kotlin_version == null) {
+ throw new IllegalArgumentException("'kotlin_snapshot_version' should be defined when building with snapshot compiler")
+ }
+ repositories {
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
+ }
+ }
+
+ repositories {
+ maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev' }
+ // kotlin-dev with space redirector
+ maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
+ mavenCentral()
+ gradlePluginPortal()
+ // For Dokka that depends on kotlinx-html
+ maven { url "https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven" }
+ mavenLocal()
+ }
+
+ configurations.classpath {
+ resolutionStrategy.eachDependency { DependencyResolveDetails details ->
+ if (details.requested.group == 'org.jetbrains.kotlin') {
+ details.useVersion kotlin_version
+ }
+ }
+ }
+
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
+ classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
+ classpath "org.jetbrains.kotlinx:kover:$kover_version"
+ classpath "org.jetbrains.kotlinx:binary-compatibility-validator:$validator_version"
+ classpath "org.jetbrains.kotlinx:kotlinx-knit:$knit_version"
+ classpath 'ru.vyarus:gradle-animalsniffer-plugin:1.5.3' // Android API check
+
+ classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.18'
+
+ // Various benchmarking stuff
+ classpath "com.github.jengelman.gradle.plugins:shadow:4.0.2"
+ classpath "me.champeau.gradle:jmh-gradle-plugin:0.5.3"
+ classpath "net.ltgt.gradle:gradle-apt-plugin:0.21"
+ }
+}
+
+// To make it visible for compiler-version.gradle
+ext.compilerVersion = org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION
+ext.nativeDebugBuild = org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType.DEBUG
+
+apply plugin: 'binary-compatibility-validator'
+apply plugin: 'base'
+apply plugin: 'kotlinx-knit'
+
+apiValidation {
+ ignoredProjects += ["benchmark", "guide", "kotlinx-serialization"]
+}
+
+knit {
+ siteRoot = "https://kotlin.github.io/kotlinx.serialization"
+ moduleDocs = "build/dokka/htmlMultiModule"
+}
+
+// Build API docs for all modules with dokka before running Knit
+knitPrepare.dependsOn "dokka"
+
+apply plugin: 'org.jetbrains.dokka'
+dependencies {
+ dokkaPlugin("org.jetbrains.kotlinx:dokka-pathsaver-plugin:$knit_version")
+}
+
+allprojects {
+ group 'org.jetbrains.kotlinx'
+
+ def deployVersion = properties['DeployVersion']
+ if (deployVersion != null) version = deployVersion
+
+ if (project.hasProperty("bootstrap")) {
+ version = version + '-SNAPSHOT'
+ }
+
+ // the only place where HostManager could be instantiated
+ project.ext.hostManager = new org.jetbrains.kotlin.konan.target.HostManager()
+
+ if (build_snapshot_train) {
+ // Snapshot-specific
+ repositories {
+ mavenLocal()
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
+ }
+ }
+
+ configurations.all {
+ resolutionStrategy.eachDependency { DependencyResolveDetails details ->
+ if (details.requested.group == 'org.jetbrains.kotlin') {
+ details.useVersion kotlin_version
+ }
+ }
+ }
+
+ repositories {
+ mavenCentral()
+ maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev' }
+ // kotlin-dev with space redirector
+ maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
+ // For Dokka that depends on kotlinx-html
+ maven { url "https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven" }
+ // For local development
+ mavenLocal()
+
+ }
+}
+
+subprojects {
+ tasks.withType(org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile).all { task ->
+ if (task.name.contains("Test") || task.name.contains("Jmh")) {
+ task.kotlinOptions.freeCompilerArgs += experimentalsInTestEnabled
+ } else {
+ task.kotlinOptions.freeCompilerArgs += experimentalsEnabled
+ }
+ }
+
+ apply from: rootProject.file('gradle/teamcity.gradle')
+ // Configure publishing for some artifacts
+ if (project.name != "benchmark" && project.name != "guide") {
+ apply from: rootProject.file('gradle/publishing.gradle')
+ }
+
+}
+
+subprojects {
+ // Can't be applied to BOM
+ if (project.name == "kotlinx-serialization-bom" || project.name == "benchmark" || project.name == "guide") return
+
+ // Animalsniffer setup
+ apply plugin: 'ru.vyarus.animalsniffer'
+
+ afterEvaluate { // Can be applied only when the project is evaluated
+ animalsniffer {
+ sourceSets = [sourceSets.main]
+ }
+ dependencies {
+ signature 'net.sf.androidscents.signature:android-api-level-14:4.0_r4@signature'
+ signature 'org.codehaus.mojo.signature:java18:1.0@signature'
+ }
+ }
+
+ // Kover setup
+ apply from: rootProject.file("gradle/kover.gradle")
+}
+
+apply from: rootProject.file('gradle/compiler-version.gradle')
+apply from: rootProject.file("gradle/dokka.gradle")
+apply from: rootProject.file("gradle/benchmark-parsing.gradle")
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
new file mode 100644
index 00000000..994e674d
--- /dev/null
+++ b/buildSrc/build.gradle.kts
@@ -0,0 +1,24 @@
+import java.util.*
+import java.io.FileInputStream
+
+plugins {
+ `kotlin-dsl`
+}
+
+repositories {
+ mavenCentral()
+}
+
+val kotlinVersion = FileInputStream(file("../gradle.properties")).use { propFile ->
+ val ver = Properties().apply { load(propFile) }["kotlin.version"]
+ require(ver is String) { "kotlin.version must be string in ../gradle.properties, got $ver instead" }
+ ver
+}
+
+dependencies {
+ implementation(kotlin("gradle-plugin", kotlinVersion))
+}
+
+kotlinDslPluginOptions {
+ experimentalWarning.set(false)
+}
diff --git a/buildSrc/src/main/kotlin/Java9Modularity.kt b/buildSrc/src/main/kotlin/Java9Modularity.kt
new file mode 100644
index 00000000..05052972
--- /dev/null
+++ b/buildSrc/src/main/kotlin/Java9Modularity.kt
@@ -0,0 +1,102 @@
+import org.gradle.api.*
+import org.gradle.api.tasks.bundling.*
+import org.gradle.api.tasks.compile.*
+import org.gradle.kotlin.dsl.*
+import org.jetbrains.kotlin.gradle.dsl.*
+import org.jetbrains.kotlin.gradle.plugin.mpp.*
+import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
+import org.jetbrains.kotlin.gradle.targets.jvm.*
+import java.io.*
+
+object Java9Modularity {
+
+ @JvmStatic
+ @JvmOverloads
+ fun Project.configureJava9ModuleInfo(multiRelease: Boolean = true) {
+ val kotlin = extensions.findByType<KotlinProjectExtension>() ?: return
+ val jvmTargets = kotlin.targets.filter { it is KotlinJvmTarget || it is KotlinWithJavaTarget<*> }
+ if (jvmTargets.isEmpty()) {
+ logger.warn("No Kotlin JVM targets found, can't configure compilation of module-info!")
+ }
+ jvmTargets.forEach { target ->
+ val artifactTask = tasks.getByName<Jar>(target.artifactsTaskName) {
+ if (multiRelease) {
+ manifest {
+ attributes("Multi-Release" to true)
+ }
+ }
+ }
+
+ target.compilations.forEach { compilation ->
+ val compileKotlinTask = compilation.compileKotlinTask as AbstractCompile
+ val defaultSourceSet = compilation.defaultSourceSet
+
+ // derive the names of the source set and compile module task
+ val sourceSetName = defaultSourceSet.name + "Module"
+ val compileModuleTaskName = compileKotlinTask.name + "Module"
+
+ kotlin.sourceSets.create(sourceSetName) {
+ val sourceFile = this.kotlin.find { it.name == "module-info.java" }
+ val targetFile = compileKotlinTask.destinationDirectory.file("../module-info.class").get().asFile
+
+ // only configure the compilation if necessary
+ if (sourceFile != null) {
+ // the default source set depends on this new source set
+ defaultSourceSet.dependsOn(this)
+
+ // register a new compile module task
+ val compileModuleTask = registerCompileModuleTask(compileModuleTaskName, compileKotlinTask, sourceFile, targetFile)
+
+ // add the resulting module descriptor to this target's artifact
+ artifactTask.dependsOn(compileModuleTask)
+ artifactTask.from(targetFile) {
+ if (multiRelease) {
+ into("META-INF/versions/9/")
+ }
+ }
+ } else {
+ logger.info("No module-info.java file found in ${this.kotlin.srcDirs}, can't configure compilation of module-info!")
+ // remove the source set to prevent Gradle warnings
+ kotlin.sourceSets.remove(this)
+ }
+ }
+ }
+ }
+ }
+
+ private fun Project.registerCompileModuleTask(taskName: String, compileTask: AbstractCompile, sourceFile: File, targetFile: File) =
+ tasks.register(taskName, JavaCompile::class) {
+ // Also add the module-info.java source file to the Kotlin compile task;
+ // the Kotlin compiler will parse and check module dependencies,
+ // but it currently won't compile to a module-info.class file.
+ compileTask.source(sourceFile)
+
+
+ // Configure the module compile task.
+ dependsOn(compileTask)
+ source(sourceFile)
+ outputs.file(targetFile)
+ classpath = files()
+ destinationDirectory.set(compileTask.destinationDirectory)
+ sourceCompatibility = JavaVersion.VERSION_1_9.toString()
+ targetCompatibility = JavaVersion.VERSION_1_9.toString()
+
+ doFirst {
+ // Provide the module path to the compiler instead of using a classpath.
+ // The module path should be the same as the classpath of the compiler.
+ options.compilerArgs = listOf(
+ "--release", "9",
+ "--module-path", compileTask.classpath.asPath,
+ "-Xlint:-requires-transitive-automatic"
+ )
+ }
+
+ doLast {
+ // Move the compiled file out of the Kotlin compile task's destination dir,
+ // so it won't disturb Gradle's caching mechanisms.
+ val compiledFile = destinationDirectory.file(targetFile.name).get().asFile
+ targetFile.parentFile.mkdirs()
+ compiledFile.renameTo(targetFile)
+ }
+ }
+}
diff --git a/buildSrc/src/main/kotlin/Publishing.kt b/buildSrc/src/main/kotlin/Publishing.kt
new file mode 100644
index 00000000..a855da97
--- /dev/null
+++ b/buildSrc/src/main/kotlin/Publishing.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+import org.gradle.api.*
+import org.gradle.api.artifacts.dsl.*
+import org.gradle.api.provider.*
+import org.gradle.api.publish.maven.*
+import org.gradle.plugins.signing.*
+import java.net.*
+
+// Pom configuration
+
+infix fun <T> Property<T>.by(value: T) {
+ set(value)
+}
+
+fun MavenPom.configureMavenCentralMetadata(project: Project) {
+ name by project.name
+ description by "Kotlin multiplatform serialization runtime library"
+ url by "https://github.com/Kotlin/kotlinx.serialization"
+
+ licenses {
+ license {
+ name by "The Apache Software License, Version 2.0"
+ url by "https://www.apache.org/licenses/LICENSE-2.0.txt"
+ distribution by "repo"
+ }
+ }
+
+ developers {
+ developer {
+ id by "JetBrains"
+ name by "JetBrains Team"
+ organization by "JetBrains"
+ organizationUrl by "https://www.jetbrains.com"
+ }
+ }
+
+ scm {
+ url by "https://github.com/Kotlin/kotlinx.serialization"
+ }
+}
+
+fun mavenRepositoryUri(): URI {
+ // TODO -SNAPSHOT detection can be made here as well
+ val repositoryId: String? = System.getenv("libs.repository.id")
+ return if (repositoryId == null) {
+ URI("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
+ } else {
+ URI("https://oss.sonatype.org/service/local/staging/deployByRepositoryId/$repositoryId")
+ }
+}
+
+fun configureMavenPublication(rh: RepositoryHandler, project: Project) {
+ rh.maven {
+ url = mavenRepositoryUri()
+ credentials {
+ username = project.getSensitiveProperty("libs.sonatype.user")
+ password = project.getSensitiveProperty("libs.sonatype.password")
+ }
+ }
+}
+
+fun signPublicationIfKeyPresent(project: Project, publication: MavenPublication) {
+ val keyId = project.getSensitiveProperty("libs.sign.key.id")
+ val signingKey = project.getSensitiveProperty("libs.sign.key.private")
+ val signingKeyPassphrase = project.getSensitiveProperty("libs.sign.passphrase")
+ if (!signingKey.isNullOrBlank()) {
+ project.extensions.configure<SigningExtension>("signing") {
+ useInMemoryPgpKeys(keyId, signingKey, signingKeyPassphrase)
+ sign(publication)
+ }
+ }
+}
+
+private fun Project.getSensitiveProperty(name: String): String? {
+ return project.findProperty(name) as? String ?: System.getenv(name)
+}
diff --git a/core/api/kotlinx-serialization-core.api b/core/api/kotlinx-serialization-core.api
new file mode 100644
index 00000000..ede49f69
--- /dev/null
+++ b/core/api/kotlinx-serialization-core.api
@@ -0,0 +1,1233 @@
+public abstract interface class kotlinx/serialization/BinaryFormat : kotlinx/serialization/SerialFormat {
+ public abstract fun decodeFromByteArray (Lkotlinx/serialization/DeserializationStrategy;[B)Ljava/lang/Object;
+ public abstract fun encodeToByteArray (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)[B
+}
+
+public abstract interface annotation class kotlinx/serialization/Contextual : java/lang/annotation/Annotation {
+}
+
+public final class kotlinx/serialization/ContextualSerializer : kotlinx/serialization/KSerializer {
+ public fun <init> (Lkotlin/reflect/KClass;)V
+ public fun <init> (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;[Lkotlinx/serialization/KSerializer;)V
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public abstract interface class kotlinx/serialization/DeserializationStrategy {
+ public abstract fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public abstract fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+}
+
+public abstract interface annotation class kotlinx/serialization/EncodeDefault : java/lang/annotation/Annotation {
+ public abstract fun mode ()Lkotlinx/serialization/EncodeDefault$Mode;
+}
+
+public final class kotlinx/serialization/EncodeDefault$Mode : java/lang/Enum {
+ public static final field ALWAYS Lkotlinx/serialization/EncodeDefault$Mode;
+ public static final field NEVER Lkotlinx/serialization/EncodeDefault$Mode;
+ public static fun valueOf (Ljava/lang/String;)Lkotlinx/serialization/EncodeDefault$Mode;
+ public static fun values ()[Lkotlinx/serialization/EncodeDefault$Mode;
+}
+
+public abstract interface annotation class kotlinx/serialization/ExperimentalSerializationApi : java/lang/annotation/Annotation {
+}
+
+public abstract interface annotation class kotlinx/serialization/InheritableSerialInfo : java/lang/annotation/Annotation {
+}
+
+public abstract interface annotation class kotlinx/serialization/InternalSerializationApi : java/lang/annotation/Annotation {
+}
+
+public abstract interface class kotlinx/serialization/KSerializer : kotlinx/serialization/DeserializationStrategy, kotlinx/serialization/SerializationStrategy {
+ public abstract fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+}
+
+public final class kotlinx/serialization/MissingFieldException : kotlinx/serialization/SerializationException {
+ public fun <init> (Ljava/lang/String;)V
+}
+
+public abstract interface annotation class kotlinx/serialization/Polymorphic : java/lang/annotation/Annotation {
+}
+
+public final class kotlinx/serialization/PolymorphicSerializer : kotlinx/serialization/internal/AbstractPolymorphicSerializer {
+ public fun <init> (Lkotlin/reflect/KClass;)V
+ public fun <init> (Lkotlin/reflect/KClass;[Ljava/lang/annotation/Annotation;)V
+ public fun getBaseClass ()Lkotlin/reflect/KClass;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class kotlinx/serialization/PolymorphicSerializerKt {
+ public static final fun findPolymorphicSerializer (Lkotlinx/serialization/internal/AbstractPolymorphicSerializer;Lkotlinx/serialization/encoding/CompositeDecoder;Ljava/lang/String;)Lkotlinx/serialization/DeserializationStrategy;
+ public static final fun findPolymorphicSerializer (Lkotlinx/serialization/internal/AbstractPolymorphicSerializer;Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)Lkotlinx/serialization/SerializationStrategy;
+}
+
+public abstract interface annotation class kotlinx/serialization/Required : java/lang/annotation/Annotation {
+}
+
+public final class kotlinx/serialization/SealedClassSerializer : kotlinx/serialization/internal/AbstractPolymorphicSerializer {
+ public fun <init> (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)V
+ public fun <init> (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;[Ljava/lang/annotation/Annotation;)V
+ public fun findPolymorphicSerializerOrNull (Lkotlinx/serialization/encoding/CompositeDecoder;Ljava/lang/String;)Lkotlinx/serialization/DeserializationStrategy;
+ public fun findPolymorphicSerializerOrNull (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)Lkotlinx/serialization/SerializationStrategy;
+ public fun getBaseClass ()Lkotlin/reflect/KClass;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+}
+
+public abstract interface class kotlinx/serialization/SerialFormat {
+ public abstract fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public final class kotlinx/serialization/SerialFormatKt {
+ public static final fun decodeFromHexString (Lkotlinx/serialization/BinaryFormat;Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/String;)Ljava/lang/Object;
+ public static final fun encodeToHexString (Lkotlinx/serialization/BinaryFormat;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/lang/String;
+}
+
+public abstract interface annotation class kotlinx/serialization/SerialInfo : java/lang/annotation/Annotation {
+}
+
+public abstract interface annotation class kotlinx/serialization/SerialName : java/lang/annotation/Annotation {
+ public abstract fun value ()Ljava/lang/String;
+}
+
+public abstract interface annotation class kotlinx/serialization/Serializable : java/lang/annotation/Annotation {
+ public abstract fun with ()Ljava/lang/Class;
+}
+
+public class kotlinx/serialization/SerializationException : java/lang/IllegalArgumentException {
+ public fun <init> ()V
+ public fun <init> (Ljava/lang/String;)V
+ public fun <init> (Ljava/lang/String;Ljava/lang/Throwable;)V
+ public fun <init> (Ljava/lang/Throwable;)V
+}
+
+public abstract interface class kotlinx/serialization/SerializationStrategy {
+ public abstract fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public abstract fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public abstract interface annotation class kotlinx/serialization/Serializer : java/lang/annotation/Annotation {
+ public abstract fun forClass ()Ljava/lang/Class;
+}
+
+public final class kotlinx/serialization/SerializersKt {
+ public static final fun serializer (Ljava/lang/reflect/Type;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/reflect/KClass;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/reflect/KType;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlinx/serialization/modules/SerializersModule;Ljava/lang/reflect/Type;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlinx/serialization/modules/SerializersModule;Lkotlin/reflect/KType;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializerOrNull (Ljava/lang/reflect/Type;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializerOrNull (Lkotlin/reflect/KClass;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializerOrNull (Lkotlin/reflect/KType;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializerOrNull (Lkotlinx/serialization/modules/SerializersModule;Ljava/lang/reflect/Type;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializerOrNull (Lkotlinx/serialization/modules/SerializersModule;Lkotlin/reflect/KType;)Lkotlinx/serialization/KSerializer;
+}
+
+public abstract interface class kotlinx/serialization/StringFormat : kotlinx/serialization/SerialFormat {
+ public abstract fun decodeFromString (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/String;)Ljava/lang/Object;
+ public abstract fun encodeToString (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/lang/String;
+}
+
+public abstract interface annotation class kotlinx/serialization/Transient : java/lang/annotation/Annotation {
+}
+
+public final class kotlinx/serialization/UnknownFieldException : kotlinx/serialization/SerializationException {
+ public fun <init> (I)V
+}
+
+public abstract interface annotation class kotlinx/serialization/UseContextualSerialization : java/lang/annotation/Annotation {
+ public abstract fun forClasses ()[Ljava/lang/Class;
+}
+
+public abstract interface annotation class kotlinx/serialization/UseSerializers : java/lang/annotation/Annotation {
+ public abstract fun serializerClasses ()[Ljava/lang/Class;
+}
+
+public final class kotlinx/serialization/builtins/BuiltinSerializersKt {
+ public static final fun ArraySerializer (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
+ public static final fun BooleanArraySerializer ()Lkotlinx/serialization/KSerializer;
+ public static final fun ByteArraySerializer ()Lkotlinx/serialization/KSerializer;
+ public static final fun CharArraySerializer ()Lkotlinx/serialization/KSerializer;
+ public static final fun DoubleArraySerializer ()Lkotlinx/serialization/KSerializer;
+ public static final fun FloatArraySerializer ()Lkotlinx/serialization/KSerializer;
+ public static final fun IntArraySerializer ()Lkotlinx/serialization/KSerializer;
+ public static final fun ListSerializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
+ public static final fun LongArraySerializer ()Lkotlinx/serialization/KSerializer;
+ public static final fun MapEntrySerializer (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
+ public static final fun MapSerializer (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
+ public static final fun PairSerializer (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
+ public static final fun SetSerializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
+ public static final fun ShortArraySerializer ()Lkotlinx/serialization/KSerializer;
+ public static final fun TripleSerializer (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
+ public static final fun getNullable (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/UByte$Companion;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/UInt$Companion;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/ULong$Companion;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/UShort$Companion;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/Unit;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/jvm/internal/BooleanCompanionObject;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/jvm/internal/ByteCompanionObject;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/jvm/internal/CharCompanionObject;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/jvm/internal/DoubleCompanionObject;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/jvm/internal/FloatCompanionObject;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/jvm/internal/IntCompanionObject;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/jvm/internal/LongCompanionObject;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/jvm/internal/ShortCompanionObject;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/jvm/internal/StringCompanionObject;)Lkotlinx/serialization/KSerializer;
+}
+
+public final class kotlinx/serialization/builtins/LongAsStringSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/builtins/LongAsStringSerializer;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Long;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;J)V
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public final class kotlinx/serialization/descriptors/ClassSerialDescriptorBuilder {
+ public final fun element (Ljava/lang/String;Lkotlinx/serialization/descriptors/SerialDescriptor;Ljava/util/List;Z)V
+ public static synthetic fun element$default (Lkotlinx/serialization/descriptors/ClassSerialDescriptorBuilder;Ljava/lang/String;Lkotlinx/serialization/descriptors/SerialDescriptor;Ljava/util/List;ZILjava/lang/Object;)V
+ public final fun getAnnotations ()Ljava/util/List;
+ public final fun getSerialName ()Ljava/lang/String;
+ public final fun isNullable ()Z
+ public final fun setAnnotations (Ljava/util/List;)V
+ public final fun setNullable (Z)V
+}
+
+public final class kotlinx/serialization/descriptors/ContextAwareKt {
+ public static final fun getCapturedKClass (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlin/reflect/KClass;
+ public static final fun getContextualDescriptor (Lkotlinx/serialization/modules/SerializersModule;Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public static final fun getPolymorphicDescriptors (Lkotlinx/serialization/modules/SerializersModule;Lkotlinx/serialization/descriptors/SerialDescriptor;)Ljava/util/List;
+}
+
+public abstract class kotlinx/serialization/descriptors/PolymorphicKind : kotlinx/serialization/descriptors/SerialKind {
+}
+
+public final class kotlinx/serialization/descriptors/PolymorphicKind$OPEN : kotlinx/serialization/descriptors/PolymorphicKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/PolymorphicKind$OPEN;
+}
+
+public final class kotlinx/serialization/descriptors/PolymorphicKind$SEALED : kotlinx/serialization/descriptors/PolymorphicKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/PolymorphicKind$SEALED;
+}
+
+public abstract class kotlinx/serialization/descriptors/PrimitiveKind : kotlinx/serialization/descriptors/SerialKind {
+}
+
+public final class kotlinx/serialization/descriptors/PrimitiveKind$BOOLEAN : kotlinx/serialization/descriptors/PrimitiveKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/PrimitiveKind$BOOLEAN;
+}
+
+public final class kotlinx/serialization/descriptors/PrimitiveKind$BYTE : kotlinx/serialization/descriptors/PrimitiveKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/PrimitiveKind$BYTE;
+}
+
+public final class kotlinx/serialization/descriptors/PrimitiveKind$CHAR : kotlinx/serialization/descriptors/PrimitiveKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/PrimitiveKind$CHAR;
+}
+
+public final class kotlinx/serialization/descriptors/PrimitiveKind$DOUBLE : kotlinx/serialization/descriptors/PrimitiveKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/PrimitiveKind$DOUBLE;
+}
+
+public final class kotlinx/serialization/descriptors/PrimitiveKind$FLOAT : kotlinx/serialization/descriptors/PrimitiveKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/PrimitiveKind$FLOAT;
+}
+
+public final class kotlinx/serialization/descriptors/PrimitiveKind$INT : kotlinx/serialization/descriptors/PrimitiveKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/PrimitiveKind$INT;
+}
+
+public final class kotlinx/serialization/descriptors/PrimitiveKind$LONG : kotlinx/serialization/descriptors/PrimitiveKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/PrimitiveKind$LONG;
+}
+
+public final class kotlinx/serialization/descriptors/PrimitiveKind$SHORT : kotlinx/serialization/descriptors/PrimitiveKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/PrimitiveKind$SHORT;
+}
+
+public final class kotlinx/serialization/descriptors/PrimitiveKind$STRING : kotlinx/serialization/descriptors/PrimitiveKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/PrimitiveKind$STRING;
+}
+
+public abstract interface class kotlinx/serialization/descriptors/SerialDescriptor {
+ public abstract fun getAnnotations ()Ljava/util/List;
+ public abstract fun getElementAnnotations (I)Ljava/util/List;
+ public abstract fun getElementDescriptor (I)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public abstract fun getElementIndex (Ljava/lang/String;)I
+ public abstract fun getElementName (I)Ljava/lang/String;
+ public abstract fun getElementsCount ()I
+ public abstract fun getKind ()Lkotlinx/serialization/descriptors/SerialKind;
+ public abstract fun getSerialName ()Ljava/lang/String;
+ public abstract fun isElementOptional (I)Z
+ public abstract fun isInline ()Z
+ public abstract fun isNullable ()Z
+}
+
+public final class kotlinx/serialization/descriptors/SerialDescriptor$DefaultImpls {
+ public static fun getAnnotations (Lkotlinx/serialization/descriptors/SerialDescriptor;)Ljava/util/List;
+ public static fun isInline (Lkotlinx/serialization/descriptors/SerialDescriptor;)Z
+ public static fun isNullable (Lkotlinx/serialization/descriptors/SerialDescriptor;)Z
+}
+
+public final class kotlinx/serialization/descriptors/SerialDescriptorKt {
+ public static final fun getElementDescriptors (Lkotlinx/serialization/descriptors/SerialDescriptor;)Ljava/lang/Iterable;
+ public static final fun getElementNames (Lkotlinx/serialization/descriptors/SerialDescriptor;)Ljava/lang/Iterable;
+}
+
+public final class kotlinx/serialization/descriptors/SerialDescriptorsKt {
+ public static final fun PrimitiveSerialDescriptor (Ljava/lang/String;Lkotlinx/serialization/descriptors/PrimitiveKind;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public static final fun SerialDescriptor (Ljava/lang/String;Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public static final fun buildClassSerialDescriptor (Ljava/lang/String;[Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public static synthetic fun buildClassSerialDescriptor$default (Ljava/lang/String;[Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public static final fun buildSerialDescriptor (Ljava/lang/String;Lkotlinx/serialization/descriptors/SerialKind;[Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public static synthetic fun buildSerialDescriptor$default (Ljava/lang/String;Lkotlinx/serialization/descriptors/SerialKind;[Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public static final fun getNullable (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public static final fun listSerialDescriptor (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public static final fun mapSerialDescriptor (Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public static final fun serialDescriptor (Lkotlin/reflect/KType;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public static final fun setSerialDescriptor (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+}
+
+public abstract class kotlinx/serialization/descriptors/SerialKind {
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class kotlinx/serialization/descriptors/SerialKind$CONTEXTUAL : kotlinx/serialization/descriptors/SerialKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/SerialKind$CONTEXTUAL;
+}
+
+public final class kotlinx/serialization/descriptors/SerialKind$ENUM : kotlinx/serialization/descriptors/SerialKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/SerialKind$ENUM;
+}
+
+public abstract class kotlinx/serialization/descriptors/StructureKind : kotlinx/serialization/descriptors/SerialKind {
+}
+
+public final class kotlinx/serialization/descriptors/StructureKind$CLASS : kotlinx/serialization/descriptors/StructureKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/StructureKind$CLASS;
+}
+
+public final class kotlinx/serialization/descriptors/StructureKind$LIST : kotlinx/serialization/descriptors/StructureKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/StructureKind$LIST;
+}
+
+public final class kotlinx/serialization/descriptors/StructureKind$MAP : kotlinx/serialization/descriptors/StructureKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/StructureKind$MAP;
+}
+
+public final class kotlinx/serialization/descriptors/StructureKind$OBJECT : kotlinx/serialization/descriptors/StructureKind {
+ public static final field INSTANCE Lkotlinx/serialization/descriptors/StructureKind$OBJECT;
+}
+
+public abstract class kotlinx/serialization/encoding/AbstractDecoder : kotlinx/serialization/encoding/CompositeDecoder, kotlinx/serialization/encoding/Decoder {
+ public fun <init> ()V
+ public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;
+ public fun decodeBoolean ()Z
+ public final fun decodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
+ public fun decodeByte ()B
+ public final fun decodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)B
+ public fun decodeChar ()C
+ public final fun decodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)C
+ public fun decodeCollectionSize (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public fun decodeDouble ()D
+ public final fun decodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)D
+ public fun decodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public fun decodeFloat ()F
+ public final fun decodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)F
+ public fun decodeInline (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Decoder;
+ public fun decodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Decoder;
+ public fun decodeInt ()I
+ public final fun decodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)I
+ public fun decodeLong ()J
+ public final fun decodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)J
+ public fun decodeNotNullMark ()Z
+ public fun decodeNull ()Ljava/lang/Void;
+ public final fun decodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun decodeNullableSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ public fun decodeSequentially ()Z
+ public fun decodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun decodeSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ public fun decodeSerializableValue (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public static synthetic fun decodeSerializableValue$default (Lkotlinx/serialization/encoding/AbstractDecoder;Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;ILjava/lang/Object;)Ljava/lang/Object;
+ public fun decodeShort ()S
+ public final fun decodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)S
+ public fun decodeString ()Ljava/lang/String;
+ public final fun decodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;
+ public fun decodeValue ()Ljava/lang/Object;
+ public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V
+}
+
+public abstract class kotlinx/serialization/encoding/AbstractEncoder : kotlinx/serialization/encoding/CompositeEncoder, kotlinx/serialization/encoding/Encoder {
+ public fun <init> ()V
+ public fun beginCollection (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder;
+ public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeEncoder;
+ public fun encodeBoolean (Z)V
+ public final fun encodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IZ)V
+ public fun encodeByte (B)V
+ public final fun encodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IB)V
+ public fun encodeChar (C)V
+ public final fun encodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IC)V
+ public fun encodeDouble (D)V
+ public final fun encodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ID)V
+ public fun encodeElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
+ public fun encodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;I)V
+ public fun encodeFloat (F)V
+ public final fun encodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IF)V
+ public fun encodeInline (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Encoder;
+ public final fun encodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Encoder;
+ public fun encodeInt (I)V
+ public final fun encodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;II)V
+ public fun encodeLong (J)V
+ public final fun encodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IJ)V
+ public fun encodeNotNullMark ()V
+ public fun encodeNull ()V
+ public fun encodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeNullableSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeShort (S)V
+ public final fun encodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IS)V
+ public fun encodeString (Ljava/lang/String;)V
+ public final fun encodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V
+ public fun encodeValue (Ljava/lang/Object;)V
+ public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V
+ public fun shouldEncodeElementDefault (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
+}
+
+public abstract interface class kotlinx/serialization/encoding/CompositeDecoder {
+ public static final field Companion Lkotlinx/serialization/encoding/CompositeDecoder$Companion;
+ public static final field DECODE_DONE I
+ public static final field UNKNOWN_NAME I
+ public abstract fun decodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
+ public abstract fun decodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)B
+ public abstract fun decodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)C
+ public abstract fun decodeCollectionSize (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public abstract fun decodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)D
+ public abstract fun decodeElementIndex (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public abstract fun decodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)F
+ public abstract fun decodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Decoder;
+ public abstract fun decodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)I
+ public abstract fun decodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)J
+ public abstract fun decodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public abstract fun decodeSequentially ()Z
+ public abstract fun decodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public abstract fun decodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)S
+ public abstract fun decodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;
+ public abstract fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V
+ public abstract fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public final class kotlinx/serialization/encoding/CompositeDecoder$Companion {
+ public static final field DECODE_DONE I
+ public static final field UNKNOWN_NAME I
+}
+
+public final class kotlinx/serialization/encoding/CompositeDecoder$DefaultImpls {
+ public static fun decodeCollectionSize (Lkotlinx/serialization/encoding/CompositeDecoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public static synthetic fun decodeNullableSerializableElement$default (Lkotlinx/serialization/encoding/CompositeDecoder;Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;ILjava/lang/Object;)Ljava/lang/Object;
+ public static fun decodeSequentially (Lkotlinx/serialization/encoding/CompositeDecoder;)Z
+ public static synthetic fun decodeSerializableElement$default (Lkotlinx/serialization/encoding/CompositeDecoder;Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;ILjava/lang/Object;)Ljava/lang/Object;
+}
+
+public abstract interface class kotlinx/serialization/encoding/CompositeEncoder {
+ public abstract fun encodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IZ)V
+ public abstract fun encodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IB)V
+ public abstract fun encodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IC)V
+ public abstract fun encodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ID)V
+ public abstract fun encodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IF)V
+ public abstract fun encodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Encoder;
+ public abstract fun encodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;II)V
+ public abstract fun encodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IJ)V
+ public abstract fun encodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public abstract fun encodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public abstract fun encodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IS)V
+ public abstract fun encodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V
+ public abstract fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V
+ public abstract fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+ public abstract fun shouldEncodeElementDefault (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
+}
+
+public final class kotlinx/serialization/encoding/CompositeEncoder$DefaultImpls {
+ public static fun shouldEncodeElementDefault (Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
+}
+
+public abstract interface class kotlinx/serialization/encoding/Decoder {
+ public abstract fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;
+ public abstract fun decodeBoolean ()Z
+ public abstract fun decodeByte ()B
+ public abstract fun decodeChar ()C
+ public abstract fun decodeDouble ()D
+ public abstract fun decodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public abstract fun decodeFloat ()F
+ public abstract fun decodeInline (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Decoder;
+ public abstract fun decodeInt ()I
+ public abstract fun decodeLong ()J
+ public abstract fun decodeNotNullMark ()Z
+ public abstract fun decodeNull ()Ljava/lang/Void;
+ public abstract fun decodeNullableSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ public abstract fun decodeSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ public abstract fun decodeShort ()S
+ public abstract fun decodeString ()Ljava/lang/String;
+ public abstract fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public final class kotlinx/serialization/encoding/Decoder$DefaultImpls {
+ public static fun decodeNullableSerializableValue (Lkotlinx/serialization/encoding/Decoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ public static fun decodeSerializableValue (Lkotlinx/serialization/encoding/Decoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/encoding/DecodingKt {
+ public static final fun decodeStructure (Lkotlinx/serialization/encoding/Decoder;Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
+}
+
+public abstract interface class kotlinx/serialization/encoding/Encoder {
+ public abstract fun beginCollection (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder;
+ public abstract fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeEncoder;
+ public abstract fun encodeBoolean (Z)V
+ public abstract fun encodeByte (B)V
+ public abstract fun encodeChar (C)V
+ public abstract fun encodeDouble (D)V
+ public abstract fun encodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;I)V
+ public abstract fun encodeFloat (F)V
+ public abstract fun encodeInline (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Encoder;
+ public abstract fun encodeInt (I)V
+ public abstract fun encodeLong (J)V
+ public abstract fun encodeNotNullMark ()V
+ public abstract fun encodeNull ()V
+ public abstract fun encodeNullableSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public abstract fun encodeSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public abstract fun encodeShort (S)V
+ public abstract fun encodeString (Ljava/lang/String;)V
+ public abstract fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public final class kotlinx/serialization/encoding/Encoder$DefaultImpls {
+ public static fun beginCollection (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder;
+ public static fun encodeNotNullMark (Lkotlinx/serialization/encoding/Encoder;)V
+ public static fun encodeNullableSerializableValue (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public static fun encodeSerializableValue (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+}
+
+public final class kotlinx/serialization/encoding/EncodingKt {
+ public static final fun encodeCollection (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlin/jvm/functions/Function1;)V
+ public static final fun encodeCollection (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/serialization/descriptors/SerialDescriptor;Ljava/util/Collection;Lkotlin/jvm/functions/Function3;)V
+ public static final fun encodeStructure (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;)V
+}
+
+public abstract class kotlinx/serialization/internal/AbstractCollectionSerializer : kotlinx/serialization/KSerializer {
+ protected abstract fun builder ()Ljava/lang/Object;
+ protected abstract fun builderSize (Ljava/lang/Object;)I
+ protected abstract fun checkCapacity (Ljava/lang/Object;I)V
+ protected abstract fun collectionIterator (Ljava/lang/Object;)Ljava/util/Iterator;
+ protected abstract fun collectionSize (Ljava/lang/Object;)I
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public final fun merge (Lkotlinx/serialization/encoding/Decoder;Ljava/lang/Object;)Ljava/lang/Object;
+ protected abstract fun readAll (Lkotlinx/serialization/encoding/CompositeDecoder;Ljava/lang/Object;II)V
+ protected abstract fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ public static synthetic fun readElement$default (Lkotlinx/serialization/internal/AbstractCollectionSerializer;Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;ZILjava/lang/Object;)V
+ public abstract fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ protected abstract fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ protected abstract fun toResult (Ljava/lang/Object;)Ljava/lang/Object;
+}
+
+public abstract class kotlinx/serialization/internal/AbstractPolymorphicSerializer : kotlinx/serialization/KSerializer {
+ public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun findPolymorphicSerializerOrNull (Lkotlinx/serialization/encoding/CompositeDecoder;Ljava/lang/String;)Lkotlinx/serialization/DeserializationStrategy;
+ public fun findPolymorphicSerializerOrNull (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)Lkotlinx/serialization/SerializationStrategy;
+ public abstract fun getBaseClass ()Lkotlin/reflect/KClass;
+ public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public final class kotlinx/serialization/internal/ArrayListSerializer : kotlinx/serialization/internal/CollectionSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;)V
+ public synthetic fun builder ()Ljava/lang/Object;
+ public synthetic fun builderSize (Ljava/lang/Object;)I
+ public synthetic fun checkCapacity (Ljava/lang/Object;I)V
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun insert (Ljava/lang/Object;ILjava/lang/Object;)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun toResult (Ljava/lang/Object;)Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/BooleanArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
+ public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/BooleanArraySerializer : kotlinx/serialization/internal/PrimitiveArraySerializer, kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/BooleanArraySerializer;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ public synthetic fun empty ()Ljava/lang/Object;
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILkotlinx/serialization/internal/PrimitiveArrayBuilder;Z)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun writeContent (Lkotlinx/serialization/encoding/CompositeEncoder;Ljava/lang/Object;I)V
+}
+
+public final class kotlinx/serialization/internal/BooleanSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/BooleanSerializer;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Boolean;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Z)V
+}
+
+public final class kotlinx/serialization/internal/ByteArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
+ public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/ByteArraySerializer : kotlinx/serialization/internal/PrimitiveArraySerializer, kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/ByteArraySerializer;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ public synthetic fun empty ()Ljava/lang/Object;
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILkotlinx/serialization/internal/PrimitiveArrayBuilder;Z)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun writeContent (Lkotlinx/serialization/encoding/CompositeEncoder;Ljava/lang/Object;I)V
+}
+
+public final class kotlinx/serialization/internal/ByteSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/ByteSerializer;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Byte;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;B)V
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public final class kotlinx/serialization/internal/CharArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
+ public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/CharArraySerializer : kotlinx/serialization/internal/PrimitiveArraySerializer, kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/CharArraySerializer;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ public synthetic fun empty ()Ljava/lang/Object;
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILkotlinx/serialization/internal/PrimitiveArrayBuilder;Z)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun writeContent (Lkotlinx/serialization/encoding/CompositeEncoder;Ljava/lang/Object;I)V
+}
+
+public final class kotlinx/serialization/internal/CharSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/CharSerializer;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Character;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;C)V
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public abstract class kotlinx/serialization/internal/CollectionLikeSerializer : kotlinx/serialization/internal/AbstractCollectionSerializer {
+ public synthetic fun <init> (Lkotlinx/serialization/KSerializer;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public abstract fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ protected abstract fun insert (Ljava/lang/Object;ILjava/lang/Object;)V
+ protected final fun readAll (Lkotlinx/serialization/encoding/CompositeDecoder;Ljava/lang/Object;II)V
+ protected fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public abstract class kotlinx/serialization/internal/CollectionSerializer : kotlinx/serialization/internal/CollectionLikeSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;)V
+ public synthetic fun collectionIterator (Ljava/lang/Object;)Ljava/util/Iterator;
+ protected fun collectionIterator (Ljava/util/Collection;)Ljava/util/Iterator;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ protected fun collectionSize (Ljava/util/Collection;)I
+}
+
+public final class kotlinx/serialization/internal/DoubleArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
+ public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/DoubleArraySerializer : kotlinx/serialization/internal/PrimitiveArraySerializer, kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/DoubleArraySerializer;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ public synthetic fun empty ()Ljava/lang/Object;
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILkotlinx/serialization/internal/PrimitiveArrayBuilder;Z)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun writeContent (Lkotlinx/serialization/encoding/CompositeEncoder;Ljava/lang/Object;I)V
+}
+
+public final class kotlinx/serialization/internal/DoubleSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/DoubleSerializer;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Double;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;D)V
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public final class kotlinx/serialization/internal/ElementMarker {
+ public fun <init> (Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function2;)V
+ public final fun mark (I)V
+ public final fun nextUnmarkedIndex ()I
+}
+
+public final class kotlinx/serialization/internal/EnumDescriptor : kotlinx/serialization/internal/PluginGeneratedSerialDescriptor {
+ public fun <init> (Ljava/lang/String;I)V
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getElementDescriptor (I)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun getKind ()Lkotlinx/serialization/descriptors/SerialKind;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class kotlinx/serialization/internal/EnumSerializer : kotlinx/serialization/KSerializer {
+ public fun <init> (Ljava/lang/String;[Ljava/lang/Enum;)V
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Enum;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Enum;)V
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class kotlinx/serialization/internal/FloatArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
+ public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/FloatArraySerializer : kotlinx/serialization/internal/PrimitiveArraySerializer, kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/FloatArraySerializer;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ public synthetic fun empty ()Ljava/lang/Object;
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILkotlinx/serialization/internal/PrimitiveArrayBuilder;Z)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun writeContent (Lkotlinx/serialization/encoding/CompositeEncoder;Ljava/lang/Object;I)V
+}
+
+public final class kotlinx/serialization/internal/FloatSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/FloatSerializer;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Float;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;F)V
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public abstract interface class kotlinx/serialization/internal/GeneratedSerializer : kotlinx/serialization/KSerializer {
+ public abstract fun childSerializers ()[Lkotlinx/serialization/KSerializer;
+ public abstract fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
+}
+
+public final class kotlinx/serialization/internal/GeneratedSerializer$DefaultImpls {
+ public static fun typeParametersSerializers (Lkotlinx/serialization/internal/GeneratedSerializer;)[Lkotlinx/serialization/KSerializer;
+}
+
+public final class kotlinx/serialization/internal/HashMapSerializer : kotlinx/serialization/internal/MapLikeSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V
+ public synthetic fun builder ()Ljava/lang/Object;
+ public synthetic fun builderSize (Ljava/lang/Object;)I
+ public synthetic fun checkCapacity (Ljava/lang/Object;I)V
+ public synthetic fun collectionIterator (Ljava/lang/Object;)Ljava/util/Iterator;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun insertKeyValuePair (Ljava/util/Map;ILjava/lang/Object;Ljava/lang/Object;)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun toResult (Ljava/lang/Object;)Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/HashSetSerializer : kotlinx/serialization/internal/CollectionSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;)V
+ public synthetic fun builder ()Ljava/lang/Object;
+ public synthetic fun builderSize (Ljava/lang/Object;)I
+ public synthetic fun checkCapacity (Ljava/lang/Object;I)V
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun insert (Ljava/lang/Object;ILjava/lang/Object;)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun toResult (Ljava/lang/Object;)Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/InlineClassDescriptor : kotlinx/serialization/internal/PluginGeneratedSerialDescriptor {
+ public fun <init> (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;)V
+ public fun equals (Ljava/lang/Object;)Z
+ public fun hashCode ()I
+ public fun isInline ()Z
+}
+
+public final class kotlinx/serialization/internal/IntArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
+ public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/IntArraySerializer : kotlinx/serialization/internal/PrimitiveArraySerializer, kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/IntArraySerializer;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ public synthetic fun empty ()Ljava/lang/Object;
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILkotlinx/serialization/internal/PrimitiveArrayBuilder;Z)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun writeContent (Lkotlinx/serialization/encoding/CompositeEncoder;Ljava/lang/Object;I)V
+}
+
+public final class kotlinx/serialization/internal/IntSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/IntSerializer;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Integer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;I)V
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public final class kotlinx/serialization/internal/JsonInternalDependenciesKt {
+ public static final fun jsonCachedSerialNames (Lkotlinx/serialization/descriptors/SerialDescriptor;)Ljava/util/Set;
+}
+
+public abstract class kotlinx/serialization/internal/KeyValueSerializer : kotlinx/serialization/KSerializer {
+ public synthetic fun <init> (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ protected abstract fun getKey (Ljava/lang/Object;)Ljava/lang/Object;
+ protected final fun getKeySerializer ()Lkotlinx/serialization/KSerializer;
+ protected abstract fun getValue (Ljava/lang/Object;)Ljava/lang/Object;
+ protected final fun getValueSerializer ()Lkotlinx/serialization/KSerializer;
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ protected abstract fun toResult (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/LinkedHashMapSerializer : kotlinx/serialization/internal/MapLikeSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V
+ public synthetic fun builder ()Ljava/lang/Object;
+ public synthetic fun builderSize (Ljava/lang/Object;)I
+ public synthetic fun checkCapacity (Ljava/lang/Object;I)V
+ public synthetic fun collectionIterator (Ljava/lang/Object;)Ljava/util/Iterator;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun insertKeyValuePair (Ljava/util/Map;ILjava/lang/Object;Ljava/lang/Object;)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun toResult (Ljava/lang/Object;)Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/LinkedHashSetSerializer : kotlinx/serialization/internal/CollectionSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;)V
+ public synthetic fun builder ()Ljava/lang/Object;
+ public synthetic fun builderSize (Ljava/lang/Object;)I
+ public synthetic fun checkCapacity (Ljava/lang/Object;I)V
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun insert (Ljava/lang/Object;ILjava/lang/Object;)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun toResult (Ljava/lang/Object;)Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/LongArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
+ public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/LongArraySerializer : kotlinx/serialization/internal/PrimitiveArraySerializer, kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/LongArraySerializer;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ public synthetic fun empty ()Ljava/lang/Object;
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILkotlinx/serialization/internal/PrimitiveArrayBuilder;Z)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun writeContent (Lkotlinx/serialization/encoding/CompositeEncoder;Ljava/lang/Object;I)V
+}
+
+public final class kotlinx/serialization/internal/LongSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/LongSerializer;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Long;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;J)V
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public final class kotlinx/serialization/internal/MapEntrySerializer : kotlinx/serialization/internal/KeyValueSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun getKey (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun getValue (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun toResult (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+}
+
+public abstract class kotlinx/serialization/internal/MapLikeSerializer : kotlinx/serialization/internal/AbstractCollectionSerializer {
+ public synthetic fun <init> (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public abstract fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public final fun getKeySerializer ()Lkotlinx/serialization/KSerializer;
+ public final fun getValueSerializer ()Lkotlinx/serialization/KSerializer;
+ protected abstract fun insertKeyValuePair (Ljava/util/Map;ILjava/lang/Object;Ljava/lang/Object;)V
+ public synthetic fun readAll (Lkotlinx/serialization/encoding/CompositeDecoder;Ljava/lang/Object;II)V
+ protected final fun readAll (Lkotlinx/serialization/encoding/CompositeDecoder;Ljava/util/Map;II)V
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ protected final fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/util/Map;Z)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public abstract class kotlinx/serialization/internal/NamedValueDecoder : kotlinx/serialization/internal/TaggedDecoder {
+ public fun <init> ()V
+ protected fun composeName (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+ protected fun elementName (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;
+ public synthetic fun getTag (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/Object;
+ protected final fun getTag (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;
+ protected final fun nested (Ljava/lang/String;)Ljava/lang/String;
+}
+
+public abstract class kotlinx/serialization/internal/NamedValueEncoder : kotlinx/serialization/internal/TaggedEncoder {
+ public fun <init> ()V
+ protected fun composeName (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+ protected fun elementName (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;
+ public synthetic fun getTag (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/Object;
+ protected final fun getTag (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;
+ protected final fun nested (Ljava/lang/String;)Ljava/lang/String;
+}
+
+public final class kotlinx/serialization/internal/NullableSerializer : kotlinx/serialization/KSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;)V
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun hashCode ()I
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public final class kotlinx/serialization/internal/ObjectSerializer : kotlinx/serialization/KSerializer {
+ public fun <init> (Ljava/lang/String;Ljava/lang/Object;)V
+ public fun <init> (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/annotation/Annotation;)V
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public final class kotlinx/serialization/internal/PairSerializer : kotlinx/serialization/internal/KeyValueSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun getKey (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun getValue (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun toResult (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/Platform_commonKt {
+ public static final fun cast (Lkotlinx/serialization/DeserializationStrategy;)Lkotlinx/serialization/DeserializationStrategy;
+ public static final fun cast (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
+ public static final fun cast (Lkotlinx/serialization/SerializationStrategy;)Lkotlinx/serialization/SerializationStrategy;
+}
+
+public final class kotlinx/serialization/internal/PluginExceptionsKt {
+ public static final fun throwArrayMissingFieldException ([I[ILkotlinx/serialization/descriptors/SerialDescriptor;)V
+ public static final fun throwMissingFieldException (IILkotlinx/serialization/descriptors/SerialDescriptor;)V
+}
+
+public class kotlinx/serialization/internal/PluginGeneratedSerialDescriptor : kotlinx/serialization/descriptors/SerialDescriptor, kotlinx/serialization/internal/CachedNames {
+ public fun <init> (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V
+ public synthetic fun <init> (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun addElement (Ljava/lang/String;Z)V
+ public static synthetic fun addElement$default (Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;Ljava/lang/String;ZILjava/lang/Object;)V
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getAnnotations ()Ljava/util/List;
+ public fun getElementAnnotations (I)Ljava/util/List;
+ public fun getElementDescriptor (I)Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public fun getElementIndex (Ljava/lang/String;)I
+ public fun getElementName (I)Ljava/lang/String;
+ public final fun getElementsCount ()I
+ public fun getKind ()Lkotlinx/serialization/descriptors/SerialKind;
+ public fun getSerialName ()Ljava/lang/String;
+ public fun getSerialNames ()Ljava/util/Set;
+ public fun hashCode ()I
+ public fun isElementOptional (I)Z
+ public fun isInline ()Z
+ public fun isNullable ()Z
+ public final fun pushAnnotation (Ljava/lang/annotation/Annotation;)V
+ public final fun pushClassAnnotation (Ljava/lang/annotation/Annotation;)V
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract class kotlinx/serialization/internal/PrimitiveArrayBuilder {
+}
+
+public abstract class kotlinx/serialization/internal/PrimitiveArraySerializer : kotlinx/serialization/internal/CollectionLikeSerializer {
+ public synthetic fun builder ()Ljava/lang/Object;
+ protected final fun builder ()Lkotlinx/serialization/internal/PrimitiveArrayBuilder;
+ public synthetic fun builderSize (Ljava/lang/Object;)I
+ protected final fun builderSize (Lkotlinx/serialization/internal/PrimitiveArrayBuilder;)I
+ public synthetic fun checkCapacity (Ljava/lang/Object;I)V
+ protected final fun checkCapacity (Lkotlinx/serialization/internal/PrimitiveArrayBuilder;I)V
+ protected final fun collectionIterator (Ljava/lang/Object;)Ljava/util/Iterator;
+ public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ protected abstract fun empty ()Ljava/lang/Object;
+ public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun insert (Ljava/lang/Object;ILjava/lang/Object;)V
+ protected final fun insert (Lkotlinx/serialization/internal/PrimitiveArrayBuilder;ILjava/lang/Object;)V
+ protected abstract fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILkotlinx/serialization/internal/PrimitiveArrayBuilder;Z)V
+ public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public synthetic fun toResult (Ljava/lang/Object;)Ljava/lang/Object;
+ protected final fun toResult (Lkotlinx/serialization/internal/PrimitiveArrayBuilder;)Ljava/lang/Object;
+ protected abstract fun writeContent (Lkotlinx/serialization/encoding/CompositeEncoder;Ljava/lang/Object;I)V
+}
+
+public final class kotlinx/serialization/internal/ReferenceArraySerializer : kotlinx/serialization/internal/CollectionLikeSerializer {
+ public fun <init> (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
+ public synthetic fun builder ()Ljava/lang/Object;
+ public synthetic fun builderSize (Ljava/lang/Object;)I
+ public synthetic fun checkCapacity (Ljava/lang/Object;I)V
+ public synthetic fun collectionIterator (Ljava/lang/Object;)Ljava/util/Iterator;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun insert (Ljava/lang/Object;ILjava/lang/Object;)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun toResult (Ljava/lang/Object;)Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/SerializationConstructorMarker {
+}
+
+public abstract interface class kotlinx/serialization/internal/SerializerFactory {
+ public abstract fun serializer ([Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
+}
+
+public final class kotlinx/serialization/internal/ShortArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
+ public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
+}
+
+public final class kotlinx/serialization/internal/ShortArraySerializer : kotlinx/serialization/internal/PrimitiveArraySerializer, kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/ShortArraySerializer;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ public synthetic fun empty ()Ljava/lang/Object;
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ public synthetic fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILkotlinx/serialization/internal/PrimitiveArrayBuilder;Z)V
+ public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun writeContent (Lkotlinx/serialization/encoding/CompositeEncoder;Ljava/lang/Object;I)V
+}
+
+public final class kotlinx/serialization/internal/ShortSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/ShortSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Short;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;S)V
+}
+
+public final class kotlinx/serialization/internal/StringSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/StringSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/String;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/String;)V
+}
+
+public abstract class kotlinx/serialization/internal/TaggedDecoder : kotlinx/serialization/encoding/CompositeDecoder, kotlinx/serialization/encoding/Decoder {
+ public fun <init> ()V
+ public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;
+ protected final fun copyTagsTo (Lkotlinx/serialization/internal/TaggedDecoder;)V
+ public final fun decodeBoolean ()Z
+ public final fun decodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
+ public final fun decodeByte ()B
+ public final fun decodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)B
+ public final fun decodeChar ()C
+ public final fun decodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)C
+ public fun decodeCollectionSize (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public final fun decodeDouble ()D
+ public final fun decodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)D
+ public final fun decodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public final fun decodeFloat ()F
+ public final fun decodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)F
+ public final fun decodeInline (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Decoder;
+ public final fun decodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Decoder;
+ public final fun decodeInt ()I
+ public final fun decodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)I
+ public final fun decodeLong ()J
+ public final fun decodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)J
+ public fun decodeNotNullMark ()Z
+ public final fun decodeNull ()Ljava/lang/Void;
+ public final fun decodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun decodeNullableSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ public fun decodeSequentially ()Z
+ public final fun decodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun decodeSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ protected fun decodeSerializableValue (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public final fun decodeShort ()S
+ public final fun decodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)S
+ public final fun decodeString ()Ljava/lang/String;
+ public final fun decodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;
+ protected fun decodeTaggedBoolean (Ljava/lang/Object;)Z
+ protected fun decodeTaggedByte (Ljava/lang/Object;)B
+ protected fun decodeTaggedChar (Ljava/lang/Object;)C
+ protected fun decodeTaggedDouble (Ljava/lang/Object;)D
+ protected fun decodeTaggedEnum (Ljava/lang/Object;Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ protected fun decodeTaggedFloat (Ljava/lang/Object;)F
+ protected fun decodeTaggedInline (Ljava/lang/Object;Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Decoder;
+ protected fun decodeTaggedInt (Ljava/lang/Object;)I
+ protected fun decodeTaggedLong (Ljava/lang/Object;)J
+ protected fun decodeTaggedNotNullMark (Ljava/lang/Object;)Z
+ protected fun decodeTaggedNull (Ljava/lang/Object;)Ljava/lang/Void;
+ protected fun decodeTaggedShort (Ljava/lang/Object;)S
+ protected fun decodeTaggedString (Ljava/lang/Object;)Ljava/lang/String;
+ protected fun decodeTaggedValue (Ljava/lang/Object;)Ljava/lang/Object;
+ public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V
+ protected final fun getCurrentTag ()Ljava/lang/Object;
+ protected final fun getCurrentTagOrNull ()Ljava/lang/Object;
+ public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+ protected abstract fun getTag (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/Object;
+ protected final fun popTag ()Ljava/lang/Object;
+ protected final fun pushTag (Ljava/lang/Object;)V
+}
+
+public abstract class kotlinx/serialization/internal/TaggedEncoder : kotlinx/serialization/encoding/CompositeEncoder, kotlinx/serialization/encoding/Encoder {
+ public fun <init> ()V
+ public fun beginCollection (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder;
+ public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeEncoder;
+ public final fun encodeBoolean (Z)V
+ public final fun encodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IZ)V
+ public final fun encodeByte (B)V
+ public final fun encodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IB)V
+ public final fun encodeChar (C)V
+ public final fun encodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IC)V
+ public final fun encodeDouble (D)V
+ public final fun encodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ID)V
+ public final fun encodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;I)V
+ public final fun encodeFloat (F)V
+ public final fun encodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IF)V
+ public final fun encodeInline (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Encoder;
+ public final fun encodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Encoder;
+ public final fun encodeInt (I)V
+ public final fun encodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;II)V
+ public final fun encodeLong (J)V
+ public final fun encodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IJ)V
+ public final fun encodeNotNullMark ()V
+ public fun encodeNull ()V
+ public fun encodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeNullableSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public final fun encodeShort (S)V
+ public final fun encodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IS)V
+ public final fun encodeString (Ljava/lang/String;)V
+ public final fun encodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V
+ protected fun encodeTaggedBoolean (Ljava/lang/Object;Z)V
+ protected fun encodeTaggedByte (Ljava/lang/Object;B)V
+ protected fun encodeTaggedChar (Ljava/lang/Object;C)V
+ protected fun encodeTaggedDouble (Ljava/lang/Object;D)V
+ protected fun encodeTaggedEnum (Ljava/lang/Object;Lkotlinx/serialization/descriptors/SerialDescriptor;I)V
+ protected fun encodeTaggedFloat (Ljava/lang/Object;F)V
+ protected fun encodeTaggedInline (Ljava/lang/Object;Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Encoder;
+ protected fun encodeTaggedInt (Ljava/lang/Object;I)V
+ protected fun encodeTaggedLong (Ljava/lang/Object;J)V
+ protected fun encodeTaggedNull (Ljava/lang/Object;)V
+ protected fun encodeTaggedShort (Ljava/lang/Object;S)V
+ protected fun encodeTaggedString (Ljava/lang/Object;Ljava/lang/String;)V
+ protected fun encodeTaggedValue (Ljava/lang/Object;Ljava/lang/Object;)V
+ protected fun endEncode (Lkotlinx/serialization/descriptors/SerialDescriptor;)V
+ public final fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V
+ protected final fun getCurrentTag ()Ljava/lang/Object;
+ protected final fun getCurrentTagOrNull ()Ljava/lang/Object;
+ public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+ protected abstract fun getTag (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/Object;
+ protected final fun popTag ()Ljava/lang/Object;
+ protected final fun pushTag (Ljava/lang/Object;)V
+ public fun shouldEncodeElementDefault (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
+}
+
+public final class kotlinx/serialization/internal/TripleSerializer : kotlinx/serialization/KSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlin/Triple;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlin/Triple;)V
+}
+
+public final class kotlinx/serialization/internal/UByteSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/UByteSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize-Wa3L5BU (Lkotlinx/serialization/encoding/Decoder;)B
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize-EK-6454 (Lkotlinx/serialization/encoding/Encoder;B)V
+}
+
+public final class kotlinx/serialization/internal/UIntSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/UIntSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize-OGnWXxg (Lkotlinx/serialization/encoding/Decoder;)I
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize-Qn1smSk (Lkotlinx/serialization/encoding/Encoder;I)V
+}
+
+public final class kotlinx/serialization/internal/ULongSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/ULongSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize-I7RO_PI (Lkotlinx/serialization/encoding/Decoder;)J
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize-2TYgG_w (Lkotlinx/serialization/encoding/Encoder;J)V
+}
+
+public final class kotlinx/serialization/internal/UShortSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/UShortSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize-BwKQO78 (Lkotlinx/serialization/encoding/Decoder;)S
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize-i8woANY (Lkotlinx/serialization/encoding/Encoder;S)V
+}
+
+public final class kotlinx/serialization/internal/UnitSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/UnitSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)V
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlin/Unit;)V
+}
+
+public final class kotlinx/serialization/modules/PolymorphicModuleBuilder {
+ public fun <init> (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
+ public final fun buildTo (Lkotlinx/serialization/modules/SerializersModuleBuilder;)V
+ public final fun default (Lkotlin/jvm/functions/Function1;)V
+ public final fun defaultDeserializer (Lkotlin/jvm/functions/Function1;)V
+ public final fun subclass (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
+}
+
+public abstract class kotlinx/serialization/modules/SerializersModule {
+ public abstract fun dumpTo (Lkotlinx/serialization/modules/SerializersModuleCollector;)V
+ public final synthetic fun getContextual (Lkotlin/reflect/KClass;)Lkotlinx/serialization/KSerializer;
+ public abstract fun getContextual (Lkotlin/reflect/KClass;Ljava/util/List;)Lkotlinx/serialization/KSerializer;
+ public static synthetic fun getContextual$default (Lkotlinx/serialization/modules/SerializersModule;Lkotlin/reflect/KClass;Ljava/util/List;ILjava/lang/Object;)Lkotlinx/serialization/KSerializer;
+ public abstract fun getPolymorphic (Lkotlin/reflect/KClass;Ljava/lang/Object;)Lkotlinx/serialization/SerializationStrategy;
+ public abstract fun getPolymorphic (Lkotlin/reflect/KClass;Ljava/lang/String;)Lkotlinx/serialization/DeserializationStrategy;
+}
+
+public final class kotlinx/serialization/modules/SerializersModuleBuilder : kotlinx/serialization/modules/SerializersModuleCollector {
+ public fun <init> ()V
+ public final fun build ()Lkotlinx/serialization/modules/SerializersModule;
+ public fun contextual (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
+ public fun contextual (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
+ public final fun include (Lkotlinx/serialization/modules/SerializersModule;)V
+ public fun polymorphic (Lkotlin/reflect/KClass;Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
+ public fun polymorphicDefault (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
+ public fun polymorphicDefaultDeserializer (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
+ public fun polymorphicDefaultSerializer (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
+}
+
+public final class kotlinx/serialization/modules/SerializersModuleBuildersKt {
+ public static final fun SerializersModule (Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/modules/SerializersModule;
+ public static final fun polymorphic (Lkotlinx/serialization/modules/SerializersModuleBuilder;Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;)V
+ public static synthetic fun polymorphic$default (Lkotlinx/serialization/modules/SerializersModuleBuilder;Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
+ public static final fun serializersModuleOf (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public abstract interface class kotlinx/serialization/modules/SerializersModuleCollector {
+ public abstract fun contextual (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
+ public abstract fun contextual (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
+ public abstract fun polymorphic (Lkotlin/reflect/KClass;Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
+ public abstract fun polymorphicDefault (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
+ public abstract fun polymorphicDefaultDeserializer (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
+ public abstract fun polymorphicDefaultSerializer (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
+}
+
+public final class kotlinx/serialization/modules/SerializersModuleCollector$DefaultImpls {
+ public static fun contextual (Lkotlinx/serialization/modules/SerializersModuleCollector;Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
+ public static fun polymorphicDefault (Lkotlinx/serialization/modules/SerializersModuleCollector;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
+}
+
+public final class kotlinx/serialization/modules/SerializersModuleKt {
+ public static final fun getEmptySerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+ public static final fun overwriteWith (Lkotlinx/serialization/modules/SerializersModule;Lkotlinx/serialization/modules/SerializersModule;)Lkotlinx/serialization/modules/SerializersModule;
+ public static final fun plus (Lkotlinx/serialization/modules/SerializersModule;Lkotlinx/serialization/modules/SerializersModule;)Lkotlinx/serialization/modules/SerializersModule;
+}
+
diff --git a/core/build.gradle b/core/build.gradle
new file mode 100644
index 00000000..900c494f
--- /dev/null
+++ b/core/build.gradle
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+apply plugin: 'kotlin-multiplatform'
+apply plugin: 'kotlinx-serialization'
+
+apply from: rootProject.file("gradle/native-targets.gradle")
+apply from: rootProject.file("gradle/configure-source-sets.gradle")
+
+kotlin {
+ sourceSets {
+ jvmTest {
+ dependencies {
+ implementation 'io.kotlintest:kotlintest:2.0.7'
+ implementation 'com.google.guava:guava:24.1.1-jre'
+ implementation 'com.google.code.gson:gson:2.8.5'
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
+ }
+ }
+ }
+}
+
+/*
+ These manifest values help kotlinx.serialization compiler plugin determine if it is compatible with a given runtime library.
+ Plugin reads them during compilation.
+
+ Implementation-Version is used to determine whether runtime library supports a given plugin feature (e.g. inline classes serialization
+ in Kotlin 1.x may require runtime library version 1.y to work).
+ Compiler plugin may enable or disable features by looking on Implementation-Version.
+
+ Require-Kotlin-Version is used to determine whether runtime library with new features can work with old compilers.
+ In ideal case, its value should always be 1.4, but some refactorings (e.g. adding a method to the Encoder interface)
+ may unexpectedly break old compilers, so it is left out as a safety net. Compiler plugins, starting from 1.4 are instructed
+ to reject runtime if runtime's Require-Kotlin-Version is greater then the current compiler.
+ */
+tasks.withType(Jar).named(kotlin.jvm().artifactsTaskName) {
+ manifest {
+ attributes(
+ "Implementation-Version": version,
+ "Require-Kotlin-Version": "1.4.30-M1",
+ )
+ }
+}
+
+Java9Modularity.configureJava9ModuleInfo(project)
diff --git a/core/commonMain/src/kotlinx/serialization/Annotations.kt b/core/commonMain/src/kotlinx/serialization/Annotations.kt
new file mode 100644
index 00000000..45b5c1c0
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/Annotations.kt
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING") // Parameters of annotations should probably be ignored, too
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlin.reflect.*
+
+/**
+ * The main entry point to the serialization process.
+ * Applying [Serializable] to the Kotlin class instructs the serialization plugin to automatically generate implementation of [KSerializer]
+ * for the current class, that can be used to serialize and deserialize the class.
+ * The generated serializer can be accessed with `T.serializer()` extension function on the class companion,
+ * both are generated by the plugin as well.
+ *
+ * ```
+ * @Serializable
+ * class MyData(val myData: AnotherData, val intProperty: Int, ...)
+ *
+ * // Produces JSON string using the generated serializer
+ * val jsonString = Json.encodeToJson(MyData.serializer(), instance)
+ * ```
+ *
+ * Additionally, the user-defined serializer can be specified using [with] parameter:
+ * ```
+ * @Serializable(with = MyAnotherDataCustomSerializer::class)
+ * class MyAnotherData(...)
+ *
+ * MyAnotherData.serializer() // <- returns MyAnotherDataCustomSerializer
+ * ```
+ *
+ * For annotated properties, specifying [with] parameter is mandatory and can be used to override
+ * serializer on the use-site without affecting the rest of the usages:
+ * ```
+ * @Serializable // By default is serialized as 3 byte components
+ * class RgbPixel(val red: Short, val green: Short, val blue: Short)
+ *
+ * @Serializable
+ * class RgbExample(
+ * @Serializable(with = RgbAsHexString::class) p1: RgpPixel, // Serialize as HEX string, e.g. #FFFF00
+ * @Serializable(with = RgbAsSingleInt::class) p2: RgpPixel, // Serialize as single integer, e.g. 16711680
+ * p3: RgpPixel // Serialize as 3 short components, e.g. { "red": 255, "green": 255, "blue": 0 }
+ * )
+ * ```
+ * In this example, each pixel will be serialized using different data representation.
+ *
+ * For classes with generic type parameters, `serializer()` function requires one additional argument per each generic type parameter:
+ * ```
+ * @Serializable
+ * class Box<T>(value: T)
+ *
+ * Box.serializer() // Doesn't compile
+ * Box.serializer(Int.serializer()) // Returns serializer for Box<Int>
+ * Box.serializer(Box.serializer(Int.serializer())) // Returns serializer for Box<Box<Int>>
+ * ```
+ *
+ * ### Implementation details
+ *
+ * In order to generate `serializer` function that is not a method on the particular instance, the class should have a companion object, either named or unnamed.
+ * Companion object is generated by the plugin if it is not declared, effectively exposing both companion and `serializer()` method to class ABI.
+ * If companion object already exists, only `serializer` method will be generated.
+ *
+ * @see UseSerializers
+ * @see Serializer
+ */
+@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS, AnnotationTarget.TYPE)
+//@Retention(AnnotationRetention.RUNTIME) // Runtime is the default retention, also see KT-41082
+public annotation class Serializable(
+ val with: KClass<out KSerializer<*>> = KSerializer::class // Default value indicates that auto-generated serializer is used
+)
+
+/**
+ * Instructs the serialization plugin to turn this class into serializer for specified class [forClass].
+ * However, it would not be used automatically. To apply it on particular class or property,
+ * use [Serializable] or [UseSerializers], or [Contextual] with runtime registration.
+ *
+ * `@Serializer(forClass)` is experimental and unstable feature that can be changed in future releases.
+ * Changes may include additional constraints on classes and objects marked with this annotation,
+ * behavioural changes and even serialized shape of the class.
+ */
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+@ExperimentalSerializationApi
+public annotation class Serializer(
+ val forClass: KClass<*> // target class to create serializer for
+)
+
+/**
+ * Overrides the name of a class or a property in the corresponding [SerialDescriptor].
+ * Names and serial names are used by text-based serial formats in order to encode the name of the class or
+ * the name of the property, e.g. by `Json`.
+ *
+ * By default, [SerialDescriptor.serialName] and [SerialDescriptor.getElementName]
+ * are associated with fully-qualified name of the target class and the name of the property respectively.
+ * Applying this annotation changes the visible name to the given [value]:
+ *
+ * ```
+ * package foo
+ *
+ * @Serializable // RegularName.serializer().descriptor.serialName is "foo.RegularName"
+ * class RegularName(val myInt: Int)
+ *
+ * @Serializable
+ * @SerialName("CustomName") // Omit package from name that is used for diagnostic and polymorphism
+ * class CustomName(@SerialName("int") val myInt: Int)
+ *
+ * // Prints "{"myInt":42}"
+ * println(Json.encodeToString(RegularName(42)))
+ * // Prints "{"int":42}"
+ * println(Json.encodeToString(CustomName(42)))
+ * ```
+ */
+@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
+// @Retention(AnnotationRetention.RUNTIME) still runtime, but KT-41082
+public annotation class SerialName(val value: String)
+
+/**
+ * Indicates that property must be present during deserialization process, despite having a default value.
+ */
+@Target(AnnotationTarget.PROPERTY)
+// @Retention(AnnotationRetention.RUNTIME) still runtime, but KT-41082
+public annotation class Required
+
+/**
+ * Marks this property invisible for the whole serialization process, including [serial descriptors][SerialDescriptor].
+ * Transient properties should have default values.
+ */
+@Target(AnnotationTarget.PROPERTY)
+// @Retention(AnnotationRetention.RUNTIME) still runtime, but KT-41082
+public annotation class Transient
+
+/**
+ * Controls whether the target property is serialized when its value is equal to a default value,
+ * regardless of the format settings.
+ * Does not affect decoding and deserialization process.
+ *
+ * Example of usage:
+ * ```
+ * @Serializable
+ * data class Foo(
+ * @EncodeDefault(ALWAYS) val a: Int = 42,
+ * @EncodeDefault(NEVER) val b: Int = 43,
+ * val c: Int = 44
+ * )
+ *
+ * Json { encodeDefaults = false }.encodeToString((Foo()) // {"a": 42}
+ * Json { encodeDefaults = true }.encodeToString((Foo()) // {"a": 42, "c":44}
+ * ```
+ *
+ * @see EncodeDefault.Mode.ALWAYS
+ * @see EncodeDefault.Mode.NEVER
+ */
+@Target(AnnotationTarget.PROPERTY)
+@ExperimentalSerializationApi
+public annotation class EncodeDefault(val mode: Mode = Mode.ALWAYS) {
+ /**
+ * Strategy for the [EncodeDefault] annotation.
+ */
+ @ExperimentalSerializationApi
+ public enum class Mode {
+ /**
+ * Configures serializer to always encode the property, even if its value is equal to its default.
+ * For annotated properties, format settings are not taken into account and
+ * [CompositeEncoder.shouldEncodeElementDefault] is not invoked.
+ */
+ ALWAYS,
+
+ /**
+ * Configures serializer not to encode the property if its value is equal to its default.
+ * For annotated properties, format settings are not taken into account and
+ * [CompositeEncoder.shouldEncodeElementDefault] is not invoked.
+ */
+ NEVER
+ }
+}
+
+/**
+ * Meta-annotation that commands the compiler plugin to handle the annotation as serialization-specific.
+ * Serialization-specific annotations are preserved in the [SerialDescriptor] and can be retrieved
+ * during serialization process with [SerialDescriptor.getElementAnnotations] for properties annotations
+ * and [SerialDescriptor.annotations] for class annotations.
+ *
+ * It is recommended to explicitly specify target for serial info annotations, whether it is [AnnotationTarget.PROPERTY], [AnnotationTarget.CLASS], or both.
+ * Keep in mind that Kotlin compiler prioritizes [function parameter target][AnnotationTarget.VALUE_PARAMETER] over [property target][AnnotationTarget.PROPERTY],
+ * so serial info annotations used on constructor-parameters-as-properties without explicit declaration-site or use-site target are not preserved.
+ */
+@Target(AnnotationTarget.ANNOTATION_CLASS)
+@Retention(AnnotationRetention.BINARY)
+@ExperimentalSerializationApi
+public annotation class SerialInfo
+
+/**
+ * Meta-annotation that commands the compiler plugin to handle the annotation as serialization-specific.
+ * Serialization-specific annotations are preserved in the [SerialDescriptor] and can be retrieved
+ * during serialization process with [SerialDescriptor.getElementAnnotations].
+ *
+ * In contrary to regular [SerialInfo], this one makes annotations inheritable:
+ * If class X marked as [Serializable] has any of its supertypes annotated with annotation A that has `@InheritableSerialInfo` on it,
+ * A appears in X's [SerialDescriptor] even if X itself is not annotated.
+ * It is possible to use A multiple times on different supertypes. Resulting X's [SerialDescriptor.annotations] would still contain
+ * only one instance of A.
+ * Note that if A has any arguments, their values should be the same across all hierarchy. Otherwise, a compilation error
+ * would be reported by the plugin.
+ *
+ * Example:
+ * ```
+ * @InheritableSerialInfo
+ * annotation class A(val value: Int)
+ *
+ * @A(1) // Annotation can also be inherited from interfaces
+ * interface I
+ *
+ * @Serializable
+ * @A(1) // Argument value is the same as in I, no compiler error
+ * abstract class Base: I
+ *
+ * @Serializable
+ * class Derived: Base()
+ *
+ * // This function returns 1.
+ * fun foo(): Int = Derived.serializer().descriptor.annotations.filterIsInstance<A>().single().value
+ * ```
+ */
+@Target(AnnotationTarget.ANNOTATION_CLASS)
+@Retention(AnnotationRetention.BINARY)
+@ExperimentalSerializationApi
+public annotation class InheritableSerialInfo
+
+
+/**
+ * Instructs the plugin to use [ContextualSerializer] on a given property or type.
+ * Context serializer is usually used when serializer for type can only be found in runtime.
+ * It is also possible to apply [ContextualSerializer] to every property of the given type,
+ * using file-level [UseContextualSerialization] annotation.
+ *
+ * @see ContextualSerializer
+ * @see UseContextualSerialization
+ */
+@Target(AnnotationTarget.PROPERTY, AnnotationTarget.TYPE)
+@Retention(AnnotationRetention.BINARY)
+public annotation class Contextual
+
+/**
+ * Instructs the plugin to use [ContextualSerializer] for every type in the current file that is listed in the [forClasses].
+ *
+ * @see Contextual
+ * @see ContextualSerializer
+ */
+@Target(AnnotationTarget.FILE)
+@Retention(AnnotationRetention.BINARY)
+public annotation class UseContextualSerialization(vararg val forClasses: KClass<*>)
+
+/**
+ * Adds [serializerClasses] to serializers resolving process inside the plugin.
+ * Each of [serializerClasses] must implement [KSerializer].
+ *
+ * Inside the file with this annotation, for each given property
+ * of type `T` in some serializable class, this list would be inspected for the presence of `KSerializer<T>`.
+ * If such serializer is present, it would be used instead of default.
+ *
+ * Main use-case for this annotation is not to write @Serializable(with=SomeSerializer::class)
+ * on each property with custom serializer.
+ *
+ * Serializers from this list have higher priority than default, but lesser priority than
+ * serializers defined on the property itself, such as [Serializable] (with=...) or [Contextual].
+ */
+@Target(AnnotationTarget.FILE)
+@Retention(AnnotationRetention.BINARY)
+public annotation class UseSerializers(vararg val serializerClasses: KClass<out KSerializer<*>>)
+
+/**
+ * Instructs the serialization plugin to use [PolymorphicSerializer] on an annotated property or type usage.
+ * When used on class, replaces its serializer with [PolymorphicSerializer] everywhere.
+ *
+ * This annotation is applied automatically to interfaces and serializable abstract classes
+ * and can be applied to open classes in addition to [Serializable] for the sake of simplicity.
+ *
+ * Does not affect sealed classes, because they are gonna be serialized with subclasses automatically
+ * with special compiler plugin support which would be added later.
+ */
+@Target(AnnotationTarget.PROPERTY, AnnotationTarget.TYPE, AnnotationTarget.CLASS)
+//@Retention(AnnotationRetention.RUNTIME) // Runtime is the default retention, also see KT-41082
+public annotation class Polymorphic
+
+/**
+ * Marks declarations that are still **experimental** in kotlinx.serialization, which means that the design of the
+ * corresponding declarations has open issues which may (or may not) lead to their changes in the future.
+ * Roughly speaking, there is a chance that those declarations will be deprecated in the near future or
+ * the semantics of their behavior may change in some way that may break some code.
+ *
+ * By default, the following categories of API are experimental:
+ *
+ * * Writing 3rd-party serialization formats
+ * * Writing non-trivial custom serializers
+ * * Implementing [SerialDescriptor] interfaces
+ * * Not-yet-stable serialization formats that require additional polishing
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS)
+@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
+public annotation class ExperimentalSerializationApi
+
+/**
+ * Public API marked with this annotation is effectively **internal**, which means
+ * it should not be used outside of `kotlinx.serialization`.
+ * Signature, semantics, source and binary compatibilities are not guaranteed for this API
+ * and will be changed without any warnings or migration aids.
+ * If you cannot avoid using internal API to solve your problem, please report your use-case to serialization's issue tracker.
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS)
+@RequiresOptIn(level = RequiresOptIn.Level.ERROR)
+public annotation class InternalSerializationApi
diff --git a/core/commonMain/src/kotlinx/serialization/ContextualSerializer.kt b/core/commonMain/src/kotlinx/serialization/ContextualSerializer.kt
new file mode 100644
index 00000000..53fd4c30
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/ContextualSerializer.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.modules.*
+import kotlin.reflect.*
+
+/**
+ * This class provides support for retrieving a serializer in runtime, instead of using the one precompiled by the serialization plugin.
+ * This serializer is enabled by [Contextual] or [UseContextualSerialization].
+ *
+ * Typical usage of `ContextualSerializer` would be a serialization of a class which does not have
+ * static serializer (e.g. Java class or class from 3rd party library);
+ * or desire to override serialized class form in one dedicated output format.
+ *
+ * Serializers are being looked for in a [SerializersModule] from the target [Encoder] or [Decoder], using statically known [KClass].
+ * To create a serial module, use [SerializersModule] factory function.
+ * To pass it to encoder and decoder, refer to particular [SerialFormat]'s documentation.
+ *
+ * Usage of contextual serializer can be demonstrated by the following example:
+ * ```
+ * import java.util.Date
+ *
+ * @Serializable
+ * class ClassWithDate(val data: String, @Contextual val timestamp: Date)
+ *
+ * val moduleForDate = serializersModule(MyISO8601DateSerializer)
+ * val json = Json { serializersModule = moduleForDate }
+ * json.encodeToString(ClassWithDate("foo", Date())
+ * ```
+ *
+ * If type of the property marked with `@Contextual` is `@Serializable` by itself, the plugin-generated serializer is
+ * used as a fallback if no serializers associated with a given type is registered in the module.
+ * The fallback serializer is determined by the static type of the property, not by its actual type.
+ */
+@ExperimentalSerializationApi
+public class ContextualSerializer<T : Any>(
+ private val serializableClass: KClass<T>,
+ private val fallbackSerializer: KSerializer<T>?,
+ typeArgumentsSerializers: Array<KSerializer<*>>
+) : KSerializer<T> {
+
+ private val typeArgumentsSerializers: List<KSerializer<*>> = typeArgumentsSerializers.asList()
+
+ private fun serializer(serializersModule: SerializersModule): KSerializer<T> =
+ serializersModule.getContextual(serializableClass, typeArgumentsSerializers) ?: fallbackSerializer ?: serializableClass.serializerNotRegistered()
+
+ // Used from the old plugins
+ @Suppress("unused")
+ public constructor(serializableClass: KClass<T>) : this(serializableClass, null, EMPTY_SERIALIZER_ARRAY)
+
+ public override val descriptor: SerialDescriptor =
+ buildSerialDescriptor("kotlinx.serialization.ContextualSerializer", SerialKind.CONTEXTUAL) {
+ annotations = fallbackSerializer?.descriptor?.annotations.orEmpty()
+ }.withContext(serializableClass)
+
+ public override fun serialize(encoder: Encoder, value: T) {
+ encoder.encodeSerializableValue(serializer(encoder.serializersModule), value)
+ }
+
+ public override fun deserialize(decoder: Decoder): T {
+ return decoder.decodeSerializableValue(serializer(decoder.serializersModule))
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/KSerializer.kt b/core/commonMain/src/kotlinx/serialization/KSerializer.kt
new file mode 100644
index 00000000..3b6c8697
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/KSerializer.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.descriptors.elementNames
+import kotlinx.serialization.encoding.*
+
+/**
+ * KSerializer is responsible for the representation of a serial form of a type [T]
+ * in terms of [encoders][Encoder] and [decoders][Decoder] and for constructing and deconstructing [T]
+ * from/to a sequence of encoding primitives. For classes marked with [@Serializable][Serializable], can be
+ * obtained from generated companion extension `.serializer()` or from [serializer<T>()][serializer] function.
+ *
+ * Serialization is decoupled from the encoding process to make it completely format-agnostic.
+ * Serialization represents a type as its serial form and is abstracted from the actual
+ * format (whether its JSON, ProtoBuf or a hashing) and unaware of the underlying storage
+ * (whether it is a string builder, byte array or a network socket), while
+ * encoding/decoding is abstracted from a particular type and its serial form and is responsible
+ * for transforming primitives ("here in an int property 'foo'" call from a serializer) into a particular
+ * format-specific representation ("for a given int, append a property name in quotation marks,
+ * then append a colon, then append an actual value" for JSON) and how to retrieve a primitive
+ * ("give me an int that is 'foo' property") from the underlying representation ("expect the next string to be 'foo',
+ * parse it, then parse colon, then parse a string until the next comma as an int and return it).
+ *
+ * Serial form consists of a structural description, declared by the [descriptor] and
+ * actual serialization and deserialization processes, defined by the corresponding
+ * [serialize] and [deserialize] methods implementation.
+ *
+ * Structural description specifies how the [T] is represented in the serial form:
+ * its [kind][SerialKind] (e.g. whether it is represented as a primitive, a list or a class),
+ * its [elements][SerialDescriptor.elementNames] and their [positional names][SerialDescriptor.getElementName].
+ *
+ * Serialization process is defined as a sequence of calls to an [Encoder], and transforms a type [T]
+ * into a stream of format-agnostic primitives that represent [T], such as "here is an int, here is a double
+ * and here is another nested object". It can be demonstrated by the example:
+ * ```
+ * class MyData(int: Int, stringList: List<String>, alwaysZero: Long)
+ *
+ * // .. serialize method of a corresponding serializer
+ * fun serialize(encoder: Encoder, value: MyData): Unit = encoder.encodeStructure(descriptor) {
+ * // encodeStructure encodes beginning and end of the structure
+ * // encode 'int' property as Int
+ * encodeIntElement(descriptor, index = 0, value.int)
+ * // encode 'stringList' property as List<String>
+ * encodeSerializableElement(descriptor, index = 1, serializer<List<String>>, value.stringList)
+ * // don't encode 'alwaysZero' property because we decided to do so
+ * } // end of the structure
+ * ```
+ *
+ * Deserialization process is symmetric and uses [Decoder].
+ */
+public interface KSerializer<T> : SerializationStrategy<T>, DeserializationStrategy<T> {
+ /**
+ * Describes the structure of the serializable representation of [T], produced
+ * by this serializer. Knowing the structure of the descriptor is required to determine
+ * the shape of the serialized form (e.g. what elements are encoded as lists and what as primitives)
+ * along with its metadata such as alternative names.
+ *
+ * The descriptor is used during serialization by encoders and decoders
+ * to introspect the type and metadata of [T]'s elements being encoded or decoded, and
+ * to introspect the type, infer the schema or to compare against the predefined schema.
+ */
+ override val descriptor: SerialDescriptor
+}
+
+/**
+ * Serialization strategy defines the serial form of a type [T], including its structural description,
+ * declared by the [descriptor] and the actual serialization process, defined by the implementation
+ * of the [serialize] method.
+ *
+ * [serialize] method takes an instance of [T] and transforms it into its serial form (a sequence of primitives),
+ * calling the corresponding [Encoder] methods.
+ *
+ * A serial form of the type is a transformation of the concrete instance into a sequence of primitive values
+ * and vice versa. The serial form is not required to completely mimic the structure of the class, for example,
+ * a specific implementation may represent multiple integer values as a single string, omit or add some
+ * values that are present in the type, but not in the instance.
+ *
+ * For a more detailed explanation of the serialization process, please refer to [KSerializer] documentation.
+ */
+public interface SerializationStrategy<in T> {
+ /**
+ * Describes the structure of the serializable representation of [T], produced
+ * by this serializer.
+ */
+ public val descriptor: SerialDescriptor
+
+ /**
+ * Serializes the [value] of type [T] using the format that is represented by the given [encoder].
+ * [serialize] method is format-agnostic and operates with a high-level structured [Encoder] API.
+ * Throws [SerializationException] if value cannot be serialized.
+ *
+ * Example of serialize method:
+ * ```
+ * class MyData(int: Int, stringList: List<String>, alwaysZero: Long)
+ *
+ * fun serialize(encoder: Encoder, value: MyData): Unit = encoder.encodeStructure(descriptor) {
+ * // encodeStructure encodes beginning and end of the structure
+ * // encode 'int' property as Int
+ * encodeIntElement(descriptor, index = 0, value.int)
+ * // encode 'stringList' property as List<String>
+ * encodeSerializableElement(descriptor, index = 1, serializer<List<String>>, value.stringList)
+ * // don't encode 'alwaysZero' property because we decided to do so
+ * } // end of the structure
+ * ```
+ */
+ public fun serialize(encoder: Encoder, value: T)
+}
+
+/**
+ * Deserialization strategy defines the serial form of a type [T], including its structural description,
+ * declared by the [descriptor] and the actual deserialization process, defined by the implementation
+ * of the [deserialize] method.
+ *
+ * [deserialize] method takes an instance of [Decoder], and, knowing the serial form of the [T],
+ * invokes primitive retrieval methods on the decoder and then transforms the received primitives
+ * to an instance of [T].
+ *
+ * A serial form of the type is a transformation of the concrete instance into a sequence of primitive values
+ * and vice versa. The serial form is not required to completely mimic the structure of the class, for example,
+ * a specific implementation may represent multiple integer values as a single string, omit or add some
+ * values that are present in the type, but not in the instance.
+ *
+ * For a more detailed explanation of the serialization process, please refer to [KSerializer] documentation.
+ */
+public interface DeserializationStrategy<T> {
+ /**
+ * Describes the structure of the serializable representation of [T], that current
+ * deserializer is able to deserialize.
+ */
+ public val descriptor: SerialDescriptor
+
+ /**
+ * Deserializes the value of type [T] using the format that is represented by the given [decoder].
+ * [deserialize] method is format-agnostic and operates with a high-level structured [Decoder] API.
+ * As long as most of the formats imply an arbitrary order of properties, deserializer should be able
+ * to decode these properties in an arbitrary order and in a format-agnostic way.
+ * For that purposes, [CompositeDecoder.decodeElementIndex]-based loop is used: decoder firstly
+ * signals property at which index it is ready to decode and then expects caller to decode
+ * property with the given index.
+ *
+ * Throws [SerializationException] if value cannot be deserialized.
+ *
+ * Example of deserialize method:
+ * ```
+ * class MyData(int: Int, stringList: List<String>, alwaysZero: Long)
+ *
+ * fun deserialize(decoder: Decoder): MyData = decoder.decodeStructure(descriptor) {
+ * // decodeStructure decodes beginning and end of the structure
+ * var int: Int? = null
+ * var list: List<String>? = null
+ * loop@ while (true) {
+ * when (val index = decodeElementIndex(descriptor)) {
+ * DECODE_DONE -> break@loop
+ * 0 -> {
+ * // Decode 'int' property as Int
+ * int = decodeIntElement(descriptor, index = 0)
+ * }
+ * 1 -> {
+ * // Decode 'stringList' property as List<String>
+ * list = decodeSerializableElement(descriptor, index = 1, serializer<List<String>>())
+ * }
+ * else -> throw SerializationException("Unexpected index $index")
+ * }
+ * }
+ * if (int == null || list == null) throwMissingFieldException()
+ * // Always use 0 as a value for alwaysZero property because we decided to do so.
+ * return MyData(int, list, alwaysZero = 0L)
+ * }
+ * ```
+ */
+ public fun deserialize(decoder: Decoder): T
+}
+
diff --git a/core/commonMain/src/kotlinx/serialization/PolymorphicSerializer.kt b/core/commonMain/src/kotlinx/serialization/PolymorphicSerializer.kt
new file mode 100644
index 00000000..311f809d
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/PolymorphicSerializer.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.modules.*
+import kotlin.reflect.*
+
+/**
+ * This class provides support for multiplatform polymorphic serialization for interfaces and abstract classes.
+ *
+ * To avoid the most common security pitfalls and reflective lookup (and potential load) of an arbitrary class,
+ * all serializable implementations of any polymorphic type must be [registered][SerializersModuleBuilder.polymorphic]
+ * in advance in the scope of base polymorphic type, efficiently preventing unbounded polymorphic serialization
+ * of an arbitrary type.
+ *
+ * Polymorphic serialization is enabled automatically by default for interfaces and [Serializable] abstract classes.
+ * To enable this feature explicitly on other types, use `@SerializableWith(PolymorphicSerializer::class)`
+ * or [Polymorphic] annotation on the property.
+ *
+ * Usage of the polymorphic serialization can be demonstrated by the following example:
+ * ```
+ * abstract class BaseRequest()
+ * @Serializable
+ * data class RequestA(val id: Int): BaseRequest()
+ * @Serializable
+ * data class RequestB(val s: String): BaseRequest()
+ *
+ * abstract class BaseResponse()
+ * @Serializable
+ * data class ResponseC(val payload: Long): BaseResponse()
+ * @Serializable
+ * data class ResponseD(val payload: ByteArray): BaseResponse()
+ *
+ * @Serializable
+ * data class Message(
+ * @Polymorphic val request: BaseRequest,
+ * @Polymorphic val response: BaseResponse
+ * )
+ * ```
+ * In this example, both request and response in `Message` are serializable with [PolymorphicSerializer].
+ *
+ * `BaseRequest` and `BaseResponse` are base classes and they are captured during compile time by the plugin.
+ * Yet [PolymorphicSerializer] for `BaseRequest` should only allow `RequestA` and `RequestB` serializers, and none of the response's serializers.
+ *
+ * This is achieved via special registration function in the module:
+ * ```
+ * val requestAndResponseModule = SerializersModule {
+ * polymorphic(BaseRequest::class) {
+ * subclass(RequestA::class)
+ * subclass(RequestB::class)
+ * }
+ * polymorphic(BaseResponse::class) {
+ * subclass(ResponseC::class)
+ * subclass(ResponseD::class)
+ * }
+ * }
+ * ```
+ *
+ * @see SerializersModule
+ * @see SerializersModuleBuilder.polymorphic
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public class PolymorphicSerializer<T : Any>(override val baseClass: KClass<T>) : AbstractPolymorphicSerializer<T>() {
+
+ @PublishedApi // See comment in SealedClassSerializer
+ internal constructor(
+ baseClass: KClass<T>,
+ classAnnotations: Array<Annotation>
+ ) : this(baseClass) {
+ _annotations = classAnnotations.asList()
+ }
+
+ private var _annotations: List<Annotation> = emptyList()
+
+ public override val descriptor: SerialDescriptor by lazy(LazyThreadSafetyMode.PUBLICATION) {
+ buildSerialDescriptor("kotlinx.serialization.Polymorphic", PolymorphicKind.OPEN) {
+ element("type", String.serializer().descriptor)
+ element(
+ "value",
+ buildSerialDescriptor("kotlinx.serialization.Polymorphic<${baseClass.simpleName}>", SerialKind.CONTEXTUAL)
+ )
+ annotations = _annotations
+ }.withContext(baseClass)
+ }
+
+ override fun toString(): String {
+ return "kotlinx.serialization.PolymorphicSerializer(baseClass: $baseClass)"
+ }
+}
+
+@InternalSerializationApi
+public fun <T : Any> AbstractPolymorphicSerializer<T>.findPolymorphicSerializer(
+ decoder: CompositeDecoder,
+ klassName: String?
+): DeserializationStrategy<out T> =
+ findPolymorphicSerializerOrNull(decoder, klassName) ?: throwSubtypeNotRegistered(klassName, baseClass)
+
+@InternalSerializationApi
+public fun <T : Any> AbstractPolymorphicSerializer<T>.findPolymorphicSerializer(
+ encoder: Encoder,
+ value: T
+): SerializationStrategy<T> =
+ findPolymorphicSerializerOrNull(encoder, value) ?: throwSubtypeNotRegistered(value::class, baseClass)
diff --git a/core/commonMain/src/kotlinx/serialization/SealedSerializer.kt b/core/commonMain/src/kotlinx/serialization/SealedSerializer.kt
new file mode 100644
index 00000000..354229ff
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/SealedSerializer.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.modules.*
+import kotlin.reflect.*
+
+/**
+ * This class provides support for multiplatform polymorphic serialization of sealed classes.
+ *
+ * In contrary to [PolymorphicSerializer], all known subclasses with serializers must be passed
+ * in `subclasses` and `subSerializers` constructor parameters.
+ * If a subclass is a sealed class itself, all its subclasses are registered as well.
+ *
+ * If a sealed hierarchy is marked with [@Serializable][Serializable], an instance of this class is provided automatically.
+ * In most of the cases, you won't need to perform any manual setup:
+ *
+ * ```
+ * @Serializable
+ * sealed class SimpleSealed {
+ * @Serializable
+ * public data class SubSealedA(val s: String) : SimpleSealed()
+ *
+ * @Serializable
+ * public data class SubSealedB(val i: Int) : SimpleSealed()
+ * }
+ *
+ * // will perform correct polymorphic serialization and deserialization:
+ * Json.encodeToString(SimpleSealed.serializer(), SubSealedA("foo"))
+ * ```
+ *
+ * However, it is possible to register additional subclasses using regular [SerializersModule].
+ * It is required when one of the subclasses is an abstract class itself:
+ *
+ * ```
+ * @Serializable
+ * sealed class ProtocolWithAbstractClass {
+ * @Serializable
+ * abstract class Message : ProtocolWithAbstractClass() {
+ * @Serializable
+ * data class StringMessage(val description: String, val message: String) : Message()
+ *
+ * @Serializable
+ * data class IntMessage(val description: String, val message: Int) : Message()
+ * }
+ *
+ * @Serializable
+ * data class ErrorMessage(val error: String) : ProtocolWithAbstractClass()
+ * }
+ * ```
+ *
+ * In this case, `ErrorMessage` would be registered automatically by the plugin,
+ * but `StringMessage` and `IntMessage` require manual registration, as described in [PolymorphicSerializer] documentation:
+ *
+ * ```
+ * val abstractContext = SerializersModule {
+ * polymorphic(ProtocolWithAbstractClass::class) {
+ * subclass(ProtocolWithAbstractClass.Message.IntMessage::class)
+ * subclass(ProtocolWithAbstractClass.Message.StringMessage::class)
+ * // no need to register ProtocolWithAbstractClass.ErrorMessage
+ * }
+ * }
+ * ```
+ */
+@InternalSerializationApi
+@OptIn(ExperimentalSerializationApi::class)
+public class SealedClassSerializer<T : Any>(
+ serialName: String,
+ override val baseClass: KClass<T>,
+ subclasses: Array<KClass<out T>>,
+ subclassSerializers: Array<KSerializer<out T>>
+) : AbstractPolymorphicSerializer<T>() {
+
+ /**
+ * This constructor is needed to store serial info annotations defined on the sealed class.
+ * Support for such annotations was added in Kotlin 1.5.30; previous plugins used primary constructor of this class
+ * directly, therefore this constructor is secondary.
+ *
+ * This constructor can (and should) became primary when Require-Kotlin-Version is raised to at least 1.5.30
+ * to remove necessity to store annotations separately and calculate descriptor via `lazy {}`.
+ *
+ * When doing this change, also migrate secondary constructors from [PolymorphicSerializer] and [ObjectSerializer].
+ */
+ @PublishedApi
+ internal constructor(
+ serialName: String,
+ baseClass: KClass<T>,
+ subclasses: Array<KClass<out T>>,
+ subclassSerializers: Array<KSerializer<out T>>,
+ classAnnotations: Array<Annotation>
+ ) : this(serialName, baseClass, subclasses, subclassSerializers) {
+ this._annotations = classAnnotations.asList()
+ }
+
+ private var _annotations: List<Annotation> = emptyList()
+
+ override val descriptor: SerialDescriptor by lazy(LazyThreadSafetyMode.PUBLICATION) {
+ buildSerialDescriptor(serialName, PolymorphicKind.SEALED) {
+ element("type", String.serializer().descriptor)
+ val elementDescriptor =
+ buildSerialDescriptor("kotlinx.serialization.Sealed<${baseClass.simpleName}>", SerialKind.CONTEXTUAL) {
+ subclassSerializers.forEach {
+ val d = it.descriptor
+ element(d.serialName, d)
+ }
+ }
+ element("value", elementDescriptor)
+ annotations = _annotations
+ }
+ }
+
+ private val class2Serializer: Map<KClass<out T>, KSerializer<out T>>
+ private val serialName2Serializer: Map<String, KSerializer<out T>>
+
+ init {
+ if (subclasses.size != subclassSerializers.size) {
+ throw IllegalArgumentException("All subclasses of sealed class ${baseClass.simpleName} should be marked @Serializable")
+ }
+
+ class2Serializer = subclasses.zip(subclassSerializers).toMap()
+ serialName2Serializer = class2Serializer.entries.groupingBy { it.value.descriptor.serialName }
+ .aggregate<Map.Entry<KClass<out T>, KSerializer<out T>>, String, Map.Entry<KClass<*>, KSerializer<out T>>>
+ { key, accumulator, element, _ ->
+ if (accumulator != null) {
+ error(
+ "Multiple sealed subclasses of '$baseClass' have the same serial name '$key':" +
+ " '${accumulator.key}', '${element.key}'"
+ )
+ }
+ element
+ }.mapValues { it.value.value }
+ }
+
+ override fun findPolymorphicSerializerOrNull(decoder: CompositeDecoder, klassName: String?): DeserializationStrategy<out T>? {
+ return serialName2Serializer[klassName] ?: super.findPolymorphicSerializerOrNull(decoder, klassName)
+ }
+
+ override fun findPolymorphicSerializerOrNull(encoder: Encoder, value: T): SerializationStrategy<T>? {
+ return (class2Serializer[value::class] ?: super.findPolymorphicSerializerOrNull(encoder, value))?.cast()
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/SerialFormat.kt b/core/commonMain/src/kotlinx/serialization/SerialFormat.kt
new file mode 100644
index 00000000..e4801a0a
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/SerialFormat.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+/**
+ * Represents an instance of a serialization format
+ * that can interact with [KSerializer] and is a supertype of all entry points for a serialization.
+ * It does not impose any restrictions on a serialized form or underlying storage, neither it exposes them.
+ *
+ * Concrete data types and API for user-interaction are responsibility of a concrete subclass or subinterface,
+ * for example [StringFormat], [BinaryFormat] or `Json`.
+ *
+ * Typically, formats have their specific [Encoder] and [Decoder] implementations
+ * as private classes and do not expose them.
+ *
+ * ### Not stable for inheritance
+ *
+ * `SerialFormat` interface is not stable for inheritance in 3rd party libraries, as new methods
+ * might be added to this interface or contracts of the existing methods can be changed.
+ *
+ * It is safe to operate with instances of `SerialFormat` and call its methods.
+ */
+public interface SerialFormat {
+ /**
+ * Contains all serializers registered by format user for [Contextual] and [Polymorphic] serialization.
+ *
+ * The same module should be exposed in the format's [Encoder] and [Decoder].
+ */
+ public val serializersModule: SerializersModule
+}
+
+/**
+ * [SerialFormat] that allows conversions to and from [ByteArray] via [encodeToByteArray] and [decodeFromByteArray] methods.
+ *
+ * ### Not stable for inheritance
+ *
+ * `BinaryFormat` interface is not stable for inheritance in 3rd party libraries, as new methods
+ * might be added to this interface or contracts of the existing methods can be changed.
+ *
+ * It is safe to operate with instances of `BinaryFormat` and call its methods.
+ */
+public interface BinaryFormat : SerialFormat {
+
+ /**
+ * Serializes and encodes the given [value] to byte array using the given [serializer].
+ */
+ public fun <T> encodeToByteArray(serializer: SerializationStrategy<T>, value: T): ByteArray
+
+ /**
+ * Decodes and deserializes the given [byte array][bytes] to the value of type [T] using the given [deserializer]
+ */
+ public fun <T> decodeFromByteArray(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T
+}
+
+/**
+ * [SerialFormat] that allows conversions to and from [String] via [encodeToString] and [decodeFromString] methods.
+ *
+ * ### Not stable for inheritance
+ *
+ * `StringFormat` interface is not stable for inheritance in 3rd party libraries, as new methods
+ * might be added to this interface or contracts of the existing methods can be changed.
+ *
+ * It is safe to operate with instances of `StringFormat` and call its methods.
+ */
+public interface StringFormat : SerialFormat {
+
+ /**
+ * Serializes and encodes the given [value] to string using the given [serializer].
+ */
+ public fun <T> encodeToString(serializer: SerializationStrategy<T>, value: T): String
+
+ /**
+ * Decodes and deserializes the given [string] to the value of type [T] using the given [deserializer]
+ */
+ public fun <T> decodeFromString(deserializer: DeserializationStrategy<T>, string: String): T
+}
+
+/**
+ * Serializes and encodes the given [value] to string using serializer retrieved from the reified type parameter.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public inline fun <reified T> StringFormat.encodeToString(value: T): String =
+ encodeToString(serializersModule.serializer(), value)
+
+/**
+ * Decodes and deserializes the given [string] to the value of type [T] using deserializer
+ * retrieved from the reified type parameter.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public inline fun <reified T> StringFormat.decodeFromString(string: String): T =
+ decodeFromString(serializersModule.serializer(), string)
+
+
+/**
+ * Serializes and encodes the given [value] to byte array, delegating it to the [BinaryFormat],
+ * and then encodes resulting bytes to hex string.
+ *
+ * Hex representation does not interfere with serialization and encoding process of the format and
+ * only applies transformation to the resulting array. It is recommended to use for debugging and
+ * testing purposes.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public fun <T> BinaryFormat.encodeToHexString(serializer: SerializationStrategy<T>, value: T): String =
+ InternalHexConverter.printHexBinary(encodeToByteArray(serializer, value), lowerCase = true)
+
+/**
+ * Decodes byte array from the given [hex] string and the decodes and deserializes it
+ * to the value of type [T], delegating it to the [BinaryFormat].
+ *
+ * This method is a counterpart to [encodeToHexString]
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public fun <T> BinaryFormat.decodeFromHexString(deserializer: DeserializationStrategy<T>, hex: String): T =
+ decodeFromByteArray(deserializer, InternalHexConverter.parseHexBinary(hex))
+
+/**
+ * Serializes and encodes the given [value] to byte array, delegating it to the [BinaryFormat],
+ * and then encodes resulting bytes to hex string.
+ *
+ * Hex representation does not interfere with serialization and encoding process of the format and
+ * only applies transformation to the resulting array. It is recommended to use for debugging and
+ * testing purposes.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public inline fun <reified T> BinaryFormat.encodeToHexString(value: T): String =
+ encodeToHexString(serializersModule.serializer(), value)
+
+/**
+ * Decodes byte array from the given [hex] string and the decodes and deserializes it
+ * to the value of type [T], delegating it to the [BinaryFormat].
+ *
+ * This method is a counterpart to [encodeToHexString]
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public inline fun <reified T> BinaryFormat.decodeFromHexString(hex: String): T =
+ decodeFromHexString(serializersModule.serializer(), hex)
+
+/**
+ * Serializes and encodes the given [value] to byte array using serializer
+ * retrieved from the reified type parameter.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public inline fun <reified T> BinaryFormat.encodeToByteArray(value: T): ByteArray =
+ encodeToByteArray(serializersModule.serializer(), value)
+
+/**
+ * Decodes and deserializes the given [byte array][bytes] to the value of type [T] using deserializer
+ * retrieved from the reified type parameter.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public inline fun <reified T> BinaryFormat.decodeFromByteArray(bytes: ByteArray): T =
+ decodeFromByteArray(serializersModule.serializer(), bytes)
diff --git a/core/commonMain/src/kotlinx/serialization/SerializationException.kt b/core/commonMain/src/kotlinx/serialization/SerializationException.kt
new file mode 100644
index 00000000..41631f50
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/SerializationException.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+/**
+ * A generic exception indicating the problem in serialization or deserialization process.
+ * This is a generic exception type that can be thrown during the problem at any stage of the serialization,
+ * including encoding, decoding, serialization, deserialization.
+ * [SerialFormat] implementors should throw subclasses of this exception at any unexpected event,
+ * whether it is a malformed input or unsupported class layout.
+ */
+public open class SerializationException : IllegalArgumentException {
+ /*
+ * Rationale behind making it IllegalArgumentException:
+ * Any serialization exception is triggered by the illegal argument, whether
+ * it is a serializer that does not support specific structure or an invalid input.
+ * Making it IAE just aligns the implementation with this fact.
+ *
+ * Another point is input validation. The simplest way to validate
+ * deserialized data is `require` in `init` block:
+ * ```
+ * @Serializable class Foo(...) {
+ * init {
+ * required(age > 0) { ... }
+ * require(name.isNotBlank()) { ... }
+ * }
+ * }
+ * ```
+ * While clearly being serialization error (when compromised data was deserialized),
+ * Kotlin way is to throw IAE here instead of using library-specific SerializationException.
+ *
+ * Also, any production-grade system has a general try-catch around deserialization of potentially
+ * untrusted/invalid/corrupted data with the corresponding logging, error reporting and diagnostic.
+ * Such handling should catch some subtype of exception (e.g. it's unlikely that catching OOM is desirable).
+ * Taking it into account, it becomes clear that SE should be subtype of IAE.
+ */
+
+ /**
+ * Creates an instance of [SerializationException] without any details.
+ */
+ public constructor()
+
+ /**
+ * Creates an instance of [SerializationException] with the specified detail [message].
+ */
+ public constructor(message: String?) : super(message)
+
+ /**
+ * Creates an instance of [SerializationException] with the specified detail [message], and the given [cause].
+ */
+ public constructor(message: String?, cause: Throwable?) : super(message, cause)
+
+ /**
+ * Creates an instance of [SerializationException] with the specified [cause].
+ */
+ public constructor(cause: Throwable?) : super(cause)
+}
+
+/**
+ * Thrown when [KSerializer] did not receive property from [Decoder], and this property was not optional.
+ */
+@PublishedApi
+internal class MissingFieldException
+// This constructor is used by coroutines exception recovery
+internal constructor(message: String?, cause: Throwable?) : SerializationException(message, cause) {
+ // This constructor is used by the generated serializers
+ constructor(fieldName: String) : this("Field '$fieldName' is required, but it was missing", null)
+ internal constructor(fieldNames: List<String>, serialName: String) : this(if (fieldNames.size == 1) "Field '${fieldNames[0]}' is required for type with serial name '$serialName', but it was missing" else "Fields $fieldNames are required for type with serial name '$serialName', but they were missing", null)
+}
+
+/**
+ * Thrown when [KSerializer] received unknown property index from [CompositeDecoder.decodeElementIndex].
+ *
+ * This exception means that data schema has changed in backwards-incompatible way.
+ */
+@PublishedApi
+internal class UnknownFieldException
+// This constructor is used by coroutines exception recovery
+internal constructor(message: String?) : SerializationException(message) {
+ // This constructor is used by the generated serializers
+ constructor(index: Int) : this("An unknown field for index $index")
+}
diff --git a/core/commonMain/src/kotlinx/serialization/Serializers.kt b/core/commonMain/src/kotlinx/serialization/Serializers.kt
new file mode 100644
index 00000000..a5211877
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/Serializers.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:Suppress("DEPRECATION_ERROR", "UNCHECKED_CAST")
+@file:JvmMultifileClass
+@file:JvmName("SerializersKt")
+
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.builtins.MapEntrySerializer
+import kotlinx.serialization.builtins.PairSerializer
+import kotlinx.serialization.builtins.TripleSerializer
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.modules.*
+import kotlin.jvm.*
+import kotlin.reflect.*
+
+/**
+ * Retrieves a serializer for the given type [T].
+ * This method is a reified version of `serializer(KType)`.
+ */
+public inline fun <reified T> serializer(): KSerializer<T> {
+ return serializer(typeOf<T>()).cast()
+}
+
+/**
+ * Retrieves serializer for the given type [T] from the current [SerializersModule] and,
+ * if not found, fallbacks to plain [serializer] method.
+ */
+public inline fun <reified T> SerializersModule.serializer(): KSerializer<T> {
+ return serializer(typeOf<T>()).cast()
+}
+
+/**
+ * Creates a serializer for the given [type].
+ * [type] argument can be obtained with experimental [typeOf] method.
+ * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable).
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public fun serializer(type: KType): KSerializer<Any?> = EmptySerializersModule.serializer(type)
+
+/**
+ * Creates a serializer for the given [type].
+ * [type] argument can be obtained with experimental [typeOf] method.
+ * Returns `null` if serializer cannot be created (provided [type] or its type argument is not serializable).
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public fun serializerOrNull(type: KType): KSerializer<Any?>? = EmptySerializersModule.serializerOrNull(type)
+
+/**
+ * Attempts to create a serializer for the given [type] and fallbacks to [contextual][SerializersModule.getContextual]
+ * lookup for non-serializable types.
+ * [type] argument can be obtained with experimental [typeOf] method.
+ * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable and is not registered in [this] module).
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public fun SerializersModule.serializer(type: KType): KSerializer<Any?> =
+ serializerByKTypeImpl(type, failOnMissingTypeArgSerializer = true) ?: type.kclass()
+ .platformSpecificSerializerNotRegistered()
+
+/**
+ * Attempts to create a serializer for the given [type] and fallbacks to [contextual][SerializersModule.getContextual]
+ * lookup for non-serializable types.
+ * [type] argument can be obtained with experimental [typeOf] method.
+ * Returns `null` if serializer cannot be created (provided [type] or its type argument is not serializable and is not registered in [this] module).
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public fun SerializersModule.serializerOrNull(type: KType): KSerializer<Any?>? {
+ return serializerByKTypeImpl(type, failOnMissingTypeArgSerializer = false)
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private fun SerializersModule.serializerByKTypeImpl(
+ type: KType,
+ failOnMissingTypeArgSerializer: Boolean
+): KSerializer<Any?>? {
+ val rootClass = type.kclass()
+ val isNullable = type.isMarkedNullable
+ val typeArguments = type.arguments
+ .map { requireNotNull(it.type) { "Star projections in type arguments are not allowed, but had $type" } }
+ val result: KSerializer<Any>? = when {
+ typeArguments.isEmpty() -> rootClass.serializerOrNull() ?: getContextual(rootClass)
+ else -> builtinSerializer(typeArguments, rootClass, failOnMissingTypeArgSerializer)
+ }?.cast()
+ return result?.nullable(isNullable)
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private fun SerializersModule.builtinSerializer(
+ typeArguments: List<KType>,
+ rootClass: KClass<Any>,
+ failOnMissingTypeArgSerializer: Boolean
+): KSerializer<out Any>? {
+ val serializers = if (failOnMissingTypeArgSerializer)
+ typeArguments.map(::serializer)
+ else {
+ typeArguments.map { serializerOrNull(it) ?: return null }
+ }
+ // Array is not supported, see KT-32839
+ return when (rootClass) {
+ Collection::class, List::class, MutableList::class, ArrayList::class -> ArrayListSerializer(serializers[0])
+ HashSet::class -> HashSetSerializer(serializers[0])
+ Set::class, MutableSet::class, LinkedHashSet::class -> LinkedHashSetSerializer(serializers[0])
+ HashMap::class -> HashMapSerializer(serializers[0], serializers[1])
+ Map::class, MutableMap::class, LinkedHashMap::class -> LinkedHashMapSerializer(
+ serializers[0],
+ serializers[1]
+ )
+ Map.Entry::class -> MapEntrySerializer(serializers[0], serializers[1])
+ Pair::class -> PairSerializer(serializers[0], serializers[1])
+ Triple::class -> TripleSerializer(serializers[0], serializers[1], serializers[2])
+ else -> {
+ if (isReferenceArray(rootClass)) {
+ return ArraySerializer<Any, Any?>(typeArguments[0].classifier as KClass<Any>, serializers[0]).cast()
+ }
+ val args = serializers.toTypedArray()
+ rootClass.constructSerializerForGivenTypeArgs(*args)
+ ?: reflectiveOrContextual(rootClass, serializers)
+ }
+ }
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal fun <T : Any> SerializersModule.reflectiveOrContextual(kClass: KClass<T>, typeArgumentsSerializers: List<KSerializer<Any?>>): KSerializer<T>? {
+ return kClass.serializerOrNull() ?: getContextual(kClass, typeArgumentsSerializers)
+}
+
+/**
+ * Retrieves a [KSerializer] for the given [KClass].
+ * The given class must be annotated with [Serializable] or be one of the built-in types.
+ *
+ * This method uses platform-specific reflection available for the given erased `KClass`
+ * and is not recommended to use this method for anything, but last-ditch resort, e.g.
+ * when all type info is lost, your application has crashed and it is the final attempt to log or send some serializable data.
+ *
+ * The recommended way to retrieve the serializer is inline [serializer] function and [`serializer(KType)`][serializer]
+ *
+ * This API is not guaranteed to work consistently across different platforms or
+ * to work in cases that slightly differ from "plain @Serializable class" and have platform and reflection specific limitations.
+ *
+ * ### Constraints
+ * This paragraph explains known (but not all!) constraints of the `serializer()` implementation.
+ * Please note that they are not bugs, but implementation restrictions that we cannot workaround.
+ *
+ * * This method may behave differently on JVM, JS and Native because of runtime reflection differences
+ * * Serializers for classes with generic parameters are ignored by this method
+ * * External serializers generated with `Serializer(forClass = )` are not lookuped consistently
+ * * Serializers for classes with named companion objects are not lookuped consistently
+ *
+ * @throws SerializationException if serializer can't be found.
+ */
+@InternalSerializationApi
+public fun <T : Any> KClass<T>.serializer(): KSerializer<T> = serializerOrNull() ?: serializerNotRegistered()
+
+/**
+ * Retrieves a [KSerializer] for the given [KClass] or returns `null` if none is found.
+ * The given class must be annotated with [Serializable] or be one of the built-in types.
+ * This method uses platform-specific reflection available for the given erased `KClass`
+ * and it is not recommended to use this method for anything, but last-ditch resort, e.g.
+ * when all type info is lost, your application has crashed and it is the final attempt to log or send some serializable data.
+ *
+ * This API is not guaranteed to work consistently across different platforms or
+ * to work in cases that slightly differ from "plain @Serializable class".
+ *
+ * ### Constraints
+ * This paragraph explains known (but not all!) constraints of the `serializerOrNull()` implementation.
+ * Please note that they are not bugs, but implementation restrictions that we cannot workaround.
+ *
+ * * This method may behave differently on JVM, JS and Native because of runtime reflection differences
+ * * Serializers for classes with generic parameters are ignored by this method
+ * * External serializers generated with `Serializer(forClass = )` are not lookuped consistently
+ * * Serializers for classes with named companion objects are not lookuped consistently
+ */
+@InternalSerializationApi
+public fun <T : Any> KClass<T>.serializerOrNull(): KSerializer<T>? =
+ compiledSerializerImpl() ?: builtinSerializerOrNull()
+
+private fun <T : Any> KSerializer<T>.nullable(shouldBeNullable: Boolean): KSerializer<T?> {
+ if (shouldBeNullable) return nullable
+ return this as KSerializer<T?>
+}
diff --git a/core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt b/core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt
new file mode 100644
index 00000000..147f25d9
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:Suppress("DEPRECATION_ERROR", "FunctionName")
+
+package kotlinx.serialization.builtins
+
+import kotlinx.serialization.*
+import kotlinx.serialization.internal.*
+import kotlin.reflect.*
+import kotlinx.serialization.descriptors.*
+
+/**
+ * Returns a nullable serializer for the given serializer of non-null type.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public val <T : Any> KSerializer<T>.nullable: KSerializer<T?>
+ get() {
+ @Suppress("UNCHECKED_CAST")
+ return if (descriptor.isNullable) (this as KSerializer<T?>) else NullableSerializer(this)
+ }
+
+/**
+ * Returns built-in serializer for Kotlin [Pair].
+ * Resulting serializer represents pair as a structure of two key-value pairs.
+ */
+public fun <K, V> PairSerializer(
+ keySerializer: KSerializer<K>,
+ valueSerializer: KSerializer<V>
+): KSerializer<Pair<K, V>> = kotlinx.serialization.internal.PairSerializer(keySerializer, valueSerializer)
+
+/**
+ * Returns built-in serializer for [Map.Entry].
+ * Resulting serializer represents entry as a structure with a single key-value pair.
+ * E.g. `Pair(1, 2)` and `Map.Entry(1, 2)` will be serialized to JSON as
+ * `{"first": 1, "second": 2}` and {"1": 2} respectively.
+ */
+public fun <K, V> MapEntrySerializer(
+ keySerializer: KSerializer<K>,
+ valueSerializer: KSerializer<V>
+): KSerializer<Map.Entry<K, V>> = kotlinx.serialization.internal.MapEntrySerializer(keySerializer, valueSerializer)
+
+/**
+ * Returns built-in serializer for Kotlin [Triple].
+ * Resulting serializer represents triple as a structure of three key-value pairs.
+ */
+public fun <A, B, C> TripleSerializer(
+ aSerializer: KSerializer<A>,
+ bSerializer: KSerializer<B>,
+ cSerializer: KSerializer<C>
+): KSerializer<Triple<A, B, C>> = kotlinx.serialization.internal.TripleSerializer(aSerializer, bSerializer, cSerializer)
+
+/**
+ * Returns serializer for [Char] with [descriptor][SerialDescriptor] of [PrimitiveKind.CHAR] kind.
+ */
+public fun Char.Companion.serializer(): KSerializer<Char> = CharSerializer
+
+/**
+ * Returns serializer for [CharArray] with [descriptor][SerialDescriptor] of [StructureKind.LIST] kind.
+ * Each element of the array is serialized one by one with [Char.Companion.serializer].
+ */
+@Suppress("UNCHECKED_CAST")
+public fun CharArraySerializer(): KSerializer<CharArray> = CharArraySerializer
+
+/**
+ * Returns serializer for [Byte] with [descriptor][SerialDescriptor] of [PrimitiveKind.BYTE] kind.
+ */
+public fun Byte.Companion.serializer(): KSerializer<Byte> = ByteSerializer
+
+/**
+ * Returns serializer for [ByteArray] with [descriptor][SerialDescriptor] of [StructureKind.LIST] kind.
+ * Each element of the array is serialized one by one with [Byte.Companion.serializer].
+ */
+public fun ByteArraySerializer(): KSerializer<ByteArray> = ByteArraySerializer
+
+/**
+ * Returns serializer for [Short] with [descriptor][SerialDescriptor] of [PrimitiveKind.SHORT] kind.
+ */
+public fun Short.Companion.serializer(): KSerializer<Short> = ShortSerializer
+
+/**
+ * Returns serializer for [ShortArray] with [descriptor][SerialDescriptor] of [StructureKind.LIST] kind.
+ * Each element of the array is serialized one by one with [Short.Companion.serializer].
+ */
+public fun ShortArraySerializer(): KSerializer<ShortArray> = ShortArraySerializer
+
+/**
+ * Returns serializer for [Int] with [descriptor][SerialDescriptor] of [PrimitiveKind.INT] kind.
+ */
+public fun Int.Companion.serializer(): KSerializer<Int> = IntSerializer
+
+/**
+ * Returns serializer for [IntArray] with [descriptor][SerialDescriptor] of [StructureKind.LIST] kind.
+ * Each element of the array is serialized one by one with [Int.Companion.serializer].
+ */
+public fun IntArraySerializer(): KSerializer<IntArray> = IntArraySerializer
+
+/**
+ * Returns serializer for [Long] with [descriptor][SerialDescriptor] of [PrimitiveKind.LONG] kind.
+ */
+public fun Long.Companion.serializer(): KSerializer<Long> = LongSerializer
+
+/**
+ * Returns serializer for [LongArray] with [descriptor][SerialDescriptor] of [StructureKind.LIST] kind.
+ * Each element of the array is serialized one by one with [Long.Companion.serializer].
+ */
+public fun LongArraySerializer(): KSerializer<LongArray> = LongArraySerializer
+
+/**
+ * Returns serializer for [Float] with [descriptor][SerialDescriptor] of [PrimitiveKind.FLOAT] kind.
+ */
+public fun Float.Companion.serializer(): KSerializer<Float> = FloatSerializer
+
+/**
+ * Returns serializer for [FloatArray] with [descriptor][SerialDescriptor] of [StructureKind.LIST] kind.
+ * Each element of the array is serialized one by one with [Float.Companion.serializer].
+ */
+public fun FloatArraySerializer(): KSerializer<FloatArray> = FloatArraySerializer
+
+/**
+ * Returns serializer for [Double] with [descriptor][SerialDescriptor] of [PrimitiveKind.DOUBLE] kind.
+ */
+public fun Double.Companion.serializer(): KSerializer<Double> = DoubleSerializer
+
+/**
+ * Returns serializer for [DoubleArray] with [descriptor][SerialDescriptor] of [StructureKind.LIST] kind.
+ * Each element of the array is serialized one by one with [Double.Companion.serializer].
+ */
+public fun DoubleArraySerializer(): KSerializer<DoubleArray> = DoubleArraySerializer
+
+/**
+ * Returns serializer for [Boolean] with [descriptor][SerialDescriptor] of [PrimitiveKind.BOOLEAN] kind.
+ */
+public fun Boolean.Companion.serializer(): KSerializer<Boolean> = BooleanSerializer
+
+/**
+ * Returns serializer for [BooleanArray] with [descriptor][SerialDescriptor] of [StructureKind.LIST] kind.
+ * Each element of the array is serialized one by one with [Boolean.Companion.serializer].
+ */
+public fun BooleanArraySerializer(): KSerializer<BooleanArray> = BooleanArraySerializer
+
+/**
+ * Returns serializer for [Unit] with [descriptor][SerialDescriptor] of [StructureKind.OBJECT] kind.
+ */
+@Suppress("unused")
+public fun Unit.serializer(): KSerializer<Unit> = UnitSerializer
+
+/**
+ * Returns serializer for [String] with [descriptor][SerialDescriptor] of [PrimitiveKind.STRING] kind.
+ */
+public fun String.Companion.serializer(): KSerializer<String> = StringSerializer
+
+/**
+ * Returns serializer for reference [Array] of type [E] with [descriptor][SerialDescriptor] of [StructureKind.LIST] kind.
+ * Each element of the array is serialized with the given [elementSerializer].
+ */
+@Suppress("UNCHECKED_CAST")
+@ExperimentalSerializationApi
+public inline fun <reified T : Any, reified E : T?> ArraySerializer(elementSerializer: KSerializer<E>): KSerializer<Array<E>> =
+ ArraySerializer<T, E>(T::class, elementSerializer)
+
+/**
+ * Returns serializer for reference [Array] of type [E] with [descriptor][SerialDescriptor] of [StructureKind.LIST] kind.
+ * Each element of the array is serialized with the given [elementSerializer].
+ */
+@ExperimentalSerializationApi
+public fun <T : Any, E : T?> ArraySerializer(
+ kClass: KClass<T>,
+ elementSerializer: KSerializer<E>
+): KSerializer<Array<E>> = ReferenceArraySerializer<T, E>(kClass, elementSerializer)
+
+/**
+ * Creates a serializer for [`List<T>`][List] for the given serializer of type [T].
+ */
+public fun <T> ListSerializer(elementSerializer: KSerializer<T>): KSerializer<List<T>> =
+ ArrayListSerializer(elementSerializer)
+
+/**
+ * Creates a serializer for [`Set<T>`][Set] for the given serializer of type [T].
+ */
+public fun <T> SetSerializer(elementSerializer: KSerializer<T>): KSerializer<Set<T>> =
+ LinkedHashSetSerializer(elementSerializer)
+
+/**
+ * Creates a serializer for [`Map<K, V>`][Map] for the given serializers for
+ * its ket type [K] and value type [V].
+ */
+public fun <K, V> MapSerializer(
+ keySerializer: KSerializer<K>,
+ valueSerializer: KSerializer<V>
+): KSerializer<Map<K, V>> = LinkedHashMapSerializer(keySerializer, valueSerializer)
+
+/**
+ * Returns serializer for [UInt].
+ */
+@ExperimentalSerializationApi
+@ExperimentalUnsignedTypes
+public fun UInt.Companion.serializer(): KSerializer<UInt> = UIntSerializer
+
+/**
+ * Returns serializer for [ULong].
+ */
+@ExperimentalSerializationApi
+@ExperimentalUnsignedTypes
+public fun ULong.Companion.serializer(): KSerializer<ULong> = ULongSerializer
+
+/**
+ * Returns serializer for [UByte].
+ */
+@ExperimentalSerializationApi
+@ExperimentalUnsignedTypes
+public fun UByte.Companion.serializer(): KSerializer<UByte> = UByteSerializer
+
+/**
+ * Returns serializer for [UShort].
+ */
+@ExperimentalSerializationApi
+@ExperimentalUnsignedTypes
+public fun UShort.Companion.serializer(): KSerializer<UShort> = UShortSerializer
diff --git a/core/commonMain/src/kotlinx/serialization/builtins/LongAsStringSerializer.kt b/core/commonMain/src/kotlinx/serialization/builtins/LongAsStringSerializer.kt
new file mode 100644
index 00000000..31d373d7
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/builtins/LongAsStringSerializer.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.builtins
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+
+/**
+ * Serializer that encodes and decodes [Long] as its string representation.
+ *
+ * Intended to be used for interoperability with external clients (mainly JavaScript ones),
+ * where numbers can't be parsed correctly if they exceed
+ * [`abs(2^53-1)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER).
+ */
+public object LongAsStringSerializer : KSerializer<Long> {
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("kotlinx.serialization.LongAsStringSerializer", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: Long) {
+ encoder.encodeString(value.toString())
+ }
+
+ override fun deserialize(decoder: Decoder): Long {
+ return decoder.decodeString().toLong()
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/descriptors/ContextAware.kt b/core/commonMain/src/kotlinx/serialization/descriptors/ContextAware.kt
new file mode 100644
index 00000000..4a6367f4
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/descriptors/ContextAware.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.descriptors
+
+import kotlinx.serialization.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.modules.*
+import kotlin.jvm.*
+import kotlin.reflect.*
+
+
+/**
+ * Retrieves [KClass] associated with serializer and its descriptor, if it was captured.
+ *
+ * For schema introspection purposes, [capturedKClass] can be used in [SerializersModule] as a key
+ * to retrieve registered descriptor at runtime.
+ * This property is intended to be used on [SerialKind.CONTEXTUAL] and [PolymorphicKind.OPEN] kinds of descriptors,
+ * where actual serializer used for a property can be determined only at runtime.
+ * Serializers which represent contextual serialization and open polymorphism (namely, [ContextualSerializer] and
+ * [PolymorphicSerializer]) capture statically known KClass in a descriptor and can expose it via this property.
+ *
+ * This property is `null` for descriptors that are not of [SerialKind.CONTEXTUAL] or [PolymorphicKind.OPEN] kinds.
+ * It _may_ be `null` for descriptors of these kinds, if captured class information is unavailable for various reasons.
+ * It means that schema introspection should be performed in an application-specific manner.
+ *
+ * ### Example
+ * Imagine we need to find all distinct properties names, which may occur in output after serializing a given class
+ * with respect to [`@Contextual`][Contextual] annotation and all possible inheritors when the class is
+ * serialized polymorphically.
+ * Then we can write following function:
+ * ```
+ * fun allDistinctNames(descriptor: SerialDescriptor, module: SerialModule) = when (descriptor.kind) {
+ * is PolymorphicKind.OPEN -> module.getPolymorphicDescriptors(descriptor)
+ * .map { it.elementNames() }.flatten().toSet()
+ * is SerialKind.CONTEXTUAL -> module.getContextualDescriptor(descriptor)
+ * ?.elementNames().orEmpty().toSet()
+ * else -> descriptor.elementNames().toSet()
+ * }
+ * ```
+ * @see SerializersModule.getContextualDescriptor
+ * @see SerializersModule.getPolymorphicDescriptors
+ */
+@ExperimentalSerializationApi
+public val SerialDescriptor.capturedKClass: KClass<*>?
+ get() = when (this) {
+ is ContextDescriptor -> kClass
+ is SerialDescriptorForNullable -> original.capturedKClass
+ else -> null
+ }
+
+/**
+ * Looks up a descriptor of serializer registered for contextual serialization in [this],
+ * using [SerialDescriptor.capturedKClass] as a key.
+ *
+ * @see SerializersModuleBuilder.contextual
+ */
+@ExperimentalSerializationApi
+public fun SerializersModule.getContextualDescriptor(descriptor: SerialDescriptor): SerialDescriptor? =
+ descriptor.capturedKClass?.let { klass -> getContextual(klass)?.descriptor }
+
+/**
+ * Retrieves a collection of descriptors which serializers are registered for polymorphic serialization in [this]
+ * with base class equal to [descriptor]'s [SerialDescriptor.capturedKClass].
+ * This method does not retrieve serializers registered with [PolymorphicModuleBuilder.defaultDeserializer]
+ * or [PolymorphicModuleBuilder.defaultSerializer].
+ *
+ * @see SerializersModule.getPolymorphic
+ * @see SerializersModuleBuilder.polymorphic
+ */
+@ExperimentalSerializationApi
+public fun SerializersModule.getPolymorphicDescriptors(descriptor: SerialDescriptor): List<SerialDescriptor> {
+ val kClass = descriptor.capturedKClass ?: return emptyList()
+ // SerializersModule is sealed class with the only implementation
+ return (this as SerialModuleImpl).polyBase2Serializers[kClass]?.values.orEmpty().map { it.descriptor }
+}
+
+/**
+ * Wraps [this] in [ContextDescriptor].
+ */
+internal fun SerialDescriptor.withContext(context: KClass<*>): SerialDescriptor =
+ ContextDescriptor(this, context)
+
+/**
+ * Descriptor that captures [kClass] and allows retrieving additional runtime information,
+ * if proper [SerializersModule] is provided.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+private class ContextDescriptor(
+ private val original: SerialDescriptor,
+ @JvmField val kClass: KClass<*>
+) : SerialDescriptor by original {
+ override val serialName = "${original.serialName}<${kClass.simpleName}>"
+
+ override fun equals(other: Any?): Boolean {
+ val another = other as? ContextDescriptor ?: return false
+ return original == another.original && another.kClass == this.kClass
+ }
+
+ override fun hashCode(): Int {
+ var result = kClass.hashCode()
+ result = 31 * result + serialName.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "ContextDescriptor(kClass: $kClass, original: $original)"
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptor.kt b/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptor.kt
new file mode 100644
index 00000000..136df8c3
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptor.kt
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.descriptors
+
+import kotlinx.serialization.*
+import kotlinx.serialization.encoding.*
+
+/**
+ * Serial descriptor is an inherent property of [KSerializer] that describes the structure of the serializable type.
+ * The structure of the serializable type is not only the property of the type, but also of the serializer as well,
+ * meaning that one type can have multiple descriptors that have completely different structure.
+ *
+ * For example, the class `class Color(val rgb: Int)` can have multiple serializable representations,
+ * such as `{"rgb": 255}`, `"#0000FF"`, `[0, 0, 255]` and `{"red": 0, "green": 0, "blue": 255}`.
+ * Representations are determined by serializers and each such serializer has its own descriptor that identifies
+ * each structure in a distinguishable and format-agnostic manner.
+ *
+ * ### Structure
+ * Serial descriptor is identified by its [name][serialName] and consists of kind, potentially empty set of
+ * children elements and additional metadata.
+ *
+ * * [serialName] uniquely identifies the descriptor (and the corresponding serializer) for non-generic types.
+ * For generic types, the actual type substitution is omitted from the string representation and the name
+ * identifies the family of the serializers without type substitutions. However, type substitution is accounted
+ * in [equals] and [hashCode] operations, meaning that descriptors of generic classes with the same name, but different type
+ * parameters, are not equal to each other.
+ * [serialName] is typically used to specify the type of the target class during serialization of polymorphic and sealed
+ * classes, for observability and diagnostics.
+ * * [Kind][SerialKind] defines what this descriptor represents: primitive, enum, object, collection et cetera.
+ * * Children elements are represented as serial descriptors as well and define the structure of the type's elements.
+ * * Metadata carries additional potentially useful information, such as [nullability][nullable], [optionality][isElementOptional]
+ * and [serial annotations][getElementAnnotations].
+ *
+ * ### Usages
+ * There are two general usages of the descriptors: THE serialization process and serialization introspection.
+ *
+ * #### Serialization
+ * Serial descriptor is used as bridge between decoders/encoders and serializers.
+ * When asking for a next element, the serializer provides an expected descriptor to the decoder, and,
+ * based on the descriptor content, decoder decides how to parse its input.
+ * In JSON, for example, when the encoder is asked to encode the next element and this element
+ * is a subtype of [List], the encoder receives a descriptor with [StructureKind.LIST] and, based on that,
+ * first writes an opening square bracket before writing the content of the list.
+ *
+ * Serial descriptor _encapsulates_ the structure of the data, so serializers can be free from
+ * format-specific details. `ListSerializer` knows nothing about JSON and square brackets, providing
+ * only the structure of the data and delegating encoding decision to the format itself.
+ *
+ * #### Introspection
+ * Another usage of a serial descriptor is type introspection without its serialization.
+ * Introspection can be used to check, whether the given serializable class complies the
+ * corresponding scheme and to generate JSON or ProtoBuf schema from the given class.
+ *
+ * ### Indices
+ * Serial descriptor API operates with children indices.
+ * For the fixed-size structures, such as regular classes, index is represented by a value in
+ * the range from zero to [elementsCount] and represent and index of the property in this class.
+ * Consequently, primitives do not have children and their element count is zero.
+ *
+ * For collections and maps, though, indices does not have fixed bound. Regular collections descriptors usually
+ * have one element (`T`, maps have two, one for keys and one for values), but potentially unlimited
+ * number of actual children values. Valid indices range is not known statically
+ * and implementations of descriptor should provide consistent and unbounded names and indices.
+ *
+ * In practice, for regular classes it is allowed to invoke `getElement*(index)` methods
+ * with an index within `0 until elementsCount` range and element at the particular index corresponds to the
+ * serializable property at the given position.
+ * For collections and maps, index parameter for `getElement*(index)` methods is effectively bound
+ * by the maximal number of collection/map elements.
+ *
+ * ### Thread-safety and mutability
+ * Serial descriptor implementation should be immutable and, thus, thread-safe.
+ *
+ * ### Equality and caching
+ * Serial descriptor can be used as a unique identifier for format-specific data or schemas and
+ * this implies the following restrictions on its `equals` and `hashCode`:
+ * *
+ *
+ * An [equals] implementation should use both [serialName] and elements structure.
+ * Comparing [elementDescriptors] directly is discouraged,
+ * because it may cause a stack overflow error, e.g. if a serializable class `T` contains elements of type `T`.
+ * To avoid it, a serial descriptor implementation should compare only descriptors
+ * of class' type parameters, in a way that `serializer<Box<Int>>().descriptor != serializer<Box<String>>().descriptor`.
+ * If type parameters are equal, descriptors structure should be compared by using children elements
+ * descriptors' [serialName]s, which correspond to class names
+ * (do not confuse with elements own names, which correspond to properties names); and/or other [SerialDescriptor]
+ * properties, such as [kind].
+ * An example of [equals] implementation:
+ * ```
+ * if (this === other) return true
+ * if (other::class != this::class) return false
+ * if (serialName != other.serialName) return false
+ * if (!typeParametersAreEqual(other)) return false
+ * if (this.elementDescriptors().map { it.serialName } != other.elementDescriptors().map { it.serialName }) return false
+ * return true
+ * ```
+ *
+ * [hashCode] implementation should use the same properties for computing the result.
+ *
+ * ### User-defined serial descriptors
+ * The best way to define a custom descriptor is to use [SerialDescriptor] builder function, where
+ * for each serializable property corresponding element is declared.
+ *
+ * Example:
+ * ```
+ * // Class with custom serializer and custom serial descriptor
+ * class Data(
+ * val intField: Int, // This field is ignored by custom serializer
+ * val longField: Long, // This field is written as long, but in serialized form is named as "_longField"
+ * val stringList: List<String> // This field is written as regular list of strings
+ * )
+ *
+ * // Descriptor for such class:
+ * SerialDescriptor("my.package.Data") {
+ * // intField is deliberately ignored by serializer -- not present in the descriptor as well
+ * element<Long>("_longField") // longField is named as _longField
+ * element("stringField", listDescriptor<String>())
+ * }
+ * ```
+ *
+ * For a classes that are represented as a single primitive value, [PrimitiveSerialDescriptor] builder function can be used instead.
+ *
+ * ### Not stable for inheritance
+ *
+ * `SerialDescriptor` interface is not stable for inheritance in 3rd party libraries, as new methods
+ * might be added to this interface or contracts of the existing methods can be changed.
+ * This interface is safe to build using [buildClassSerialDescriptor] and [PrimitiveSerialDescriptor],
+ * and is safe to delegate implementation to existing instances.
+ */
+public interface SerialDescriptor {
+ /**
+ * Serial name of the descriptor that identifies pair of the associated serializer and target class.
+ *
+ * For generated serializers, serial name is equal to the corresponding class's fully-qualified name
+ * or, if overridden, [SerialName].
+ * Custom serializers should provide a unique serial name that identify both the serializable class and
+ * the serializer itself, ignoring type arguments, if they are present.
+ */
+ @ExperimentalSerializationApi
+ public val serialName: String
+
+ /**
+ * The kind of the serialized form that determines **the shape** of the serialized data.
+ * Formats use serial kind to add and parse serializer-agnostic metadata to the result.
+ *
+ * For example, JSON format wraps [classes][StructureKind.CLASS] and [StructureKind.MAP] into
+ * brackets, while ProtoBuf just serialize these types in separate ways.
+ *
+ * Kind should be consistent with the implementation, for example, if it is a [primitive][PrimitiveKind],
+ * then its elements count should be zero and vice versa.
+ */
+ @ExperimentalSerializationApi
+ public val kind: SerialKind
+
+ /**
+ * Whether the descriptor describes nullable element.
+ * Returns `true` if associated serializer can serialize/deserialize nullable elements of the described type.
+ */
+ @ExperimentalSerializationApi
+ public val isNullable: Boolean get() = false
+
+ /**
+ * Returns `true` if this descriptor describes a serializable inline class.
+ */
+ @ExperimentalSerializationApi
+ public val isInline: Boolean get() = false
+
+ /**
+ * The number of elements this descriptor describes, besides from the class itself.
+ * [elementsCount] describes the number of **semantic** elements, not the number
+ * of actual fields/properties in the serialized form, even though they frequently match.
+ *
+ * For example, for the following class
+ * `class Complex(val real: Long, val imaginary: Long)` the corresponding descriptor
+ * and the serialized form both have two elements, while for `class IntList : ArrayList<Int>()`
+ * the corresponding descriptor has a single element (`IntDescriptor`, the type of list element),
+ * but from zero up to `Int.MAX_VALUE` values in the serialized form.
+ */
+ @ExperimentalSerializationApi
+ public val elementsCount: Int
+
+ /**
+ * Returns serial annotations of the associated class.
+ * Serial annotations can be used to specify an additional metadata that may be used during serialization.
+ * Only annotations marked with [SerialInfo] are added to the resulting list.
+ */
+ @ExperimentalSerializationApi
+ public val annotations: List<Annotation> get() = emptyList()
+
+ /**
+ * Returns a positional name of the child at the given [index].
+ * Positional name represents a corresponding property name in the class, associated with
+ * the current descriptor.
+ *
+ * @throws IndexOutOfBoundsException for an illegal [index] values.
+ * @throws IllegalStateException if the current descriptor does not support children elements (e.g. is a primitive)
+ */
+ @ExperimentalSerializationApi
+ public fun getElementName(index: Int): String
+
+ /**
+ * Returns an index in the children list of the given element by its name or [CompositeDecoder.UNKNOWN_NAME]
+ * if there is no such element.
+ * The resulting index, if it is not [CompositeDecoder.UNKNOWN_NAME], is guaranteed to be usable with [getElementName].
+ */
+ @ExperimentalSerializationApi
+ public fun getElementIndex(name: String): Int
+
+ /**
+ * Returns serial annotations of the child element at the given [index].
+ * This method differs from `getElementDescriptor(index).annotations` by reporting only
+ * declaration-specific annotations:
+ * ```
+ * @Serializable
+ * @SomeSerialAnnotation
+ * class Nested(...)
+ *
+ * @Serializable
+ * class Outer(@AnotherSerialAnnotation val nested: Nested)
+ *
+ * outerDescriptor.getElementAnnotations(0) // Returns [@AnotherSerialAnnotation]
+ * outerDescriptor.getElementDescriptor(0).annotations // Returns [@SomeSerialAnnotation]
+ * ```
+ * Only annotations marked with [SerialInfo] are added to the resulting list.
+ *
+ * @throws IndexOutOfBoundsException for an illegal [index] values.
+ * @throws IllegalStateException if the current descriptor does not support children elements (e.g. is a primitive).
+ */
+ @ExperimentalSerializationApi
+ public fun getElementAnnotations(index: Int): List<Annotation>
+
+ /**
+ * Retrieves the descriptor of the child element for the given [index].
+ * For the property of type `T` on the position `i`, `getElementDescriptor(i)` yields the same result
+ * as for `T.serializer().descriptor`, if the serializer for this property is not explicitly overridden
+ * with `@Serializable(with = ...`)`, [Polymorphic] or [Contextual].
+ * This method can be used to completely introspect the type that the current descriptor describes.
+ *
+ * @throws IndexOutOfBoundsException for illegal [index] values.
+ * @throws IllegalStateException if the current descriptor does not support children elements (e.g. is a primitive).
+ */
+ @ExperimentalSerializationApi
+ public fun getElementDescriptor(index: Int): SerialDescriptor
+
+ /**
+ * Whether the element at the given [index] is optional (can be absent is serialized form).
+ * For generated descriptors, all elements that have a corresponding default parameter value are
+ * marked as optional. Custom serializers can treat optional values in a serialization-specific manner
+ * without default parameters constraint.
+ *
+ * Example of optionality:
+ * ```
+ * @Serializable
+ * class Holder(
+ * val a: Int, // Optional == false
+ * val b: Int?, // Optional == false
+ * val c: Int? = null, // Optional == true
+ * val d: List<Int>, // Optional == false
+ * val e: List<Int> = listOf(1), // Optional == true
+ * )
+ * ```
+ * Returns `false` for valid indices of collections, maps and enums.
+ *
+ * @throws IndexOutOfBoundsException for an illegal [index] values.
+ * @throws IllegalStateException if the current descriptor does not support children elements (e.g. is a primitive).
+ */
+ @ExperimentalSerializationApi
+ public fun isElementOptional(index: Int): Boolean
+}
+
+/**
+ * Returns an iterable of all descriptor [elements][SerialDescriptor.getElementDescriptor].
+ */
+@ExperimentalSerializationApi
+public val SerialDescriptor.elementDescriptors: Iterable<SerialDescriptor>
+ get() = Iterable {
+ object : Iterator<SerialDescriptor> {
+ private var elementsLeft = elementsCount
+ override fun hasNext(): Boolean = elementsLeft > 0
+
+ override fun next(): SerialDescriptor {
+ return getElementDescriptor(elementsCount - (elementsLeft--))
+ }
+ }
+ }
+
+/**
+ * Returns an iterable of all descriptor [element names][SerialDescriptor.getElementName].
+ */
+@ExperimentalSerializationApi
+public val SerialDescriptor.elementNames: Iterable<String>
+ get() = Iterable {
+ object : Iterator<String> {
+ private var elementsLeft = elementsCount
+ override fun hasNext(): Boolean = elementsLeft > 0
+
+ override fun next(): String {
+ return getElementName(elementsCount - (elementsLeft--))
+ }
+ }
+ }
diff --git a/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt b/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt
new file mode 100644
index 00000000..f9e51737
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.descriptors
+
+import kotlinx.serialization.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.internal.*
+import kotlin.reflect.*
+
+/**
+ * Builder for [SerialDescriptor].
+ * The resulting descriptor will be uniquely identified by the given [serialName], [typeParameters] and
+ * elements structure described in [builderAction] function.
+ *
+ * Example:
+ * ```
+ * // Class with custom serializer and custom serial descriptor
+ * class Data(
+ * val intField: Int, // This field is ignored by custom serializer
+ * val longField: Long, // This field is written as long, but in serialized form is named as "_longField"
+ * val stringList: List<String> // This field is written as regular list of strings
+ * val nullableInt: Int?
+ * )
+ * // Descriptor for such class:
+ * SerialDescriptor("my.package.Data") {
+ * // intField is deliberately ignored by serializer -- not present in the descriptor as well
+ * element<Long>("_longField") // longField is named as _longField
+ * element("stringField", listDescriptor<String>())
+ * element("nullableInt", descriptor<Int>().nullable)
+ * }
+ * ```
+ *
+ * Example for generic classes:
+ * ```
+ * @Serializable(CustomSerializer::class)
+ * class BoxedList<T>(val list: List<T>)
+ *
+ * class CustomSerializer<T>(tSerializer: KSerializer<T>): KSerializer<BoxedList<T>> {
+ * // here we use tSerializer.descriptor because it represents T
+ * override val descriptor = SerialDescriptor("pkg.BoxedList", CLASS, tSerializer.descriptor) {
+ * // here we have to wrap it with List first, because property has type List<T>
+ * element("list", ListSerializer(tSerializer).descriptor) // or listSerialDescriptor(tSerializer.descriptor)
+ * }
+ * }
+ * ```
+ */
+@Suppress("FunctionName")
+@OptIn(ExperimentalSerializationApi::class)
+public fun buildClassSerialDescriptor(
+ serialName: String,
+ vararg typeParameters: SerialDescriptor,
+ builderAction: ClassSerialDescriptorBuilder.() -> Unit = {}
+): SerialDescriptor {
+ require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
+ val sdBuilder = ClassSerialDescriptorBuilder(serialName)
+ sdBuilder.builderAction()
+ return SerialDescriptorImpl(
+ serialName,
+ StructureKind.CLASS,
+ sdBuilder.elementNames.size,
+ typeParameters.toList(),
+ sdBuilder
+ )
+}
+
+/**
+ * Factory to create a trivial primitive descriptors.
+ * Primitive descriptors should be used when the serialized form of the data has a primitive form, for example:
+ * ```
+ * object LongAsStringSerializer : KSerializer<Long> {
+ * override val descriptor: SerialDescriptor =
+ * PrimitiveDescriptor("kotlinx.serialization.LongAsStringSerializer", PrimitiveKind.STRING)
+ *
+ * override fun serialize(encoder: Encoder, value: Long) {
+ * encoder.encodeString(value.toString())
+ * }
+ *
+ * override fun deserialize(decoder: Decoder): Long {
+ * return decoder.decodeString().toLong()
+ * }
+ * }
+ * ```
+ */
+public fun PrimitiveSerialDescriptor(serialName: String, kind: PrimitiveKind): SerialDescriptor {
+ require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
+ return PrimitiveDescriptorSafe(serialName, kind)
+}
+
+/**
+ * Factory to create a new descriptor that is identical to [original] except that the name is equal to [serialName].
+ * Should be used when you want to serialize a type as another non-primitive type.
+ * Don't use this if you want to serialize a type as a primitive value, use [PrimitiveSerialDescriptor] instead.
+ *
+ * Example:
+ * ```
+ * @Serializable(CustomSerializer::class)
+ * class CustomType(val a: Int, val b: Int, val c: Int)
+ *
+ * class CustomSerializer: KSerializer<CustomType> {
+ * override val descriptor = SerialDescriptor("CustomType", IntArraySerializer().descriptor)
+ *
+ * override fun serialize(encoder: Encoder, value: CustomType) {
+ * encoder.encodeSerializableValue(IntArraySerializer(), intArrayOf(value.a, value.b, value.c))
+ * }
+ *
+ * override fun deserialize(decoder: Decoder): CustomType {
+ * val array = decoder.decodeSerializableValue(IntArraySerializer())
+ * return CustomType(array[0], array[1], array[2])
+ * }
+ * }
+ * ```
+ */
+@ExperimentalSerializationApi
+public fun SerialDescriptor(serialName: String, original: SerialDescriptor): SerialDescriptor {
+ require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
+ require(original.kind !is PrimitiveKind) { "For primitive descriptors please use 'PrimitiveSerialDescriptor' instead" }
+ require(serialName != original.serialName) { "The name of the wrapped descriptor ($serialName) cannot be the same as the name of the original descriptor (${original.serialName})" }
+
+ return WrappedSerialDescriptor(serialName, original)
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal class WrappedSerialDescriptor(override val serialName: String, original: SerialDescriptor) : SerialDescriptor by original
+
+/**
+ * An unsafe alternative to [buildClassSerialDescriptor] that supports an arbitrary [SerialKind].
+ * This function is left public only for migration of pre-release users and is not intended to be used
+ * as generally-safe and stable mechanism. Beware that it can produce inconsistent or non spec-compliant instances.
+ *
+ * If you end up using this builder, please file an issue with your use-case in kotlinx.serialization
+ */
+@InternalSerializationApi
+@OptIn(ExperimentalSerializationApi::class)
+public fun buildSerialDescriptor(
+ serialName: String,
+ kind: SerialKind,
+ vararg typeParameters: SerialDescriptor,
+ builder: ClassSerialDescriptorBuilder.() -> Unit = {}
+): SerialDescriptor {
+ require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
+ require(kind != StructureKind.CLASS) { "For StructureKind.CLASS please use 'buildClassSerialDescriptor' instead" }
+ val sdBuilder = ClassSerialDescriptorBuilder(serialName)
+ sdBuilder.builder()
+ return SerialDescriptorImpl(serialName, kind, sdBuilder.elementNames.size, typeParameters.toList(), sdBuilder)
+}
+
+
+/**
+ * Retrieves descriptor of type [T] using reified [serializer] function.
+ */
+public inline fun <reified T> serialDescriptor(): SerialDescriptor = serializer<T>().descriptor
+
+/**
+ * Retrieves descriptor of type associated with the given [KType][type]
+ */
+public fun serialDescriptor(type: KType): SerialDescriptor = serializer(type).descriptor
+
+/**
+ * Creates a descriptor for the type `List<T>` where `T` is the type associated with [elementDescriptor].
+ */
+@ExperimentalSerializationApi
+public fun listSerialDescriptor(elementDescriptor: SerialDescriptor): SerialDescriptor {
+ return ArrayListClassDesc(elementDescriptor)
+}
+
+/**
+ * Creates a descriptor for the type `List<T>`.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> listSerialDescriptor(): SerialDescriptor {
+ return listSerialDescriptor(serializer<T>().descriptor)
+}
+
+/**
+ * Creates a descriptor for the type `Map<K, V>` where `K` and `V` are types
+ * associated with [keyDescriptor] and [valueDescriptor] respectively.
+ */
+@ExperimentalSerializationApi
+public fun mapSerialDescriptor(
+ keyDescriptor: SerialDescriptor,
+ valueDescriptor: SerialDescriptor
+): SerialDescriptor {
+ return HashMapClassDesc(keyDescriptor, valueDescriptor)
+}
+
+/**
+ * Creates a descriptor for the type `Map<K, V>`.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified K, reified V> mapSerialDescriptor(): SerialDescriptor {
+ return mapSerialDescriptor(serializer<K>().descriptor, serializer<V>().descriptor)
+}
+
+/**
+ * Creates a descriptor for the type `Set<T>` where `T` is the type associated with [elementDescriptor].
+ */
+@ExperimentalSerializationApi
+public fun setSerialDescriptor(elementDescriptor: SerialDescriptor): SerialDescriptor {
+ return HashSetClassDesc(elementDescriptor)
+}
+
+/**
+ * Creates a descriptor for the type `Set<T>`.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> setSerialDescriptor(): SerialDescriptor {
+ return setSerialDescriptor(serializer<T>().descriptor)
+}
+
+/**
+ * Returns new serial descriptor for the same type with [isNullable][SerialDescriptor.isNullable]
+ * property set to `true`.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public val SerialDescriptor.nullable: SerialDescriptor
+ get() {
+ if (this.isNullable) return this
+ return SerialDescriptorForNullable(this)
+ }
+
+/**
+ * Builder for [SerialDescriptor] for user-defined serializers.
+ *
+ * Both explicit builder functions and implicit (using reified type-parameters) are present and are equivalent.
+ * For example, `element<Int?>("nullableIntField")` is indistinguishable from
+ * `element("nullableIntField", IntSerializer.descriptor.nullable)` and
+ * from `element("nullableIntField", descriptor<Int?>)`.
+ *
+ * Please refer to [SerialDescriptor] builder function for a complete example.
+ */
+public class ClassSerialDescriptorBuilder internal constructor(
+ public val serialName: String
+) {
+
+ /**
+ * Indicates that serializer associated with the current serial descriptor
+ * support nullable types, meaning that it should declare nullable type
+ * in its [KSerializer] type parameter and handle nulls during encoding and decoding.
+ */
+ @ExperimentalSerializationApi
+ public var isNullable: Boolean = false
+
+ /**
+ * [Serial][SerialInfo] annotations on a target type.
+ */
+ @ExperimentalSerializationApi
+ public var annotations: List<Annotation> = emptyList()
+
+ internal val elementNames: MutableList<String> = ArrayList()
+ private val uniqueNames: MutableSet<String> = HashSet()
+ internal val elementDescriptors: MutableList<SerialDescriptor> = ArrayList()
+ internal val elementAnnotations: MutableList<List<Annotation>> = ArrayList()
+ internal val elementOptionality: MutableList<Boolean> = ArrayList()
+
+ /**
+ * Add an element with a given [name][elementName], [descriptor],
+ * type annotations and optionality the resulting descriptor.
+ *
+ * Example of usage:
+ * ```
+ * class Data(
+ * val intField: Int? = null, // Optional, has default value
+ * @ProtoNumber(1) val longField: Long
+ * )
+ *
+ * // Corresponding descriptor
+ * SerialDescriptor("package.Data") {
+ * element<Int?>("intField", isOptional = true)
+ * element<Long>("longField", annotations = listOf(protoIdAnnotationInstance))
+ * }
+ * ```
+ */
+ public fun element(
+ elementName: String,
+ descriptor: SerialDescriptor,
+ annotations: List<Annotation> = emptyList(),
+ isOptional: Boolean = false
+ ) {
+ require(uniqueNames.add(elementName)) { "Element with name '$elementName' is already registered" }
+ elementNames += elementName
+ elementDescriptors += descriptor
+ elementAnnotations += annotations
+ elementOptionality += isOptional
+ }
+}
+
+/**
+ * A reified version of [element] function that
+ * extract descriptor using `serializer<T>().descriptor` call with all the restrictions of `serializer<T>().descriptor`.
+ */
+public inline fun <reified T> ClassSerialDescriptorBuilder.element(
+ elementName: String,
+ annotations: List<Annotation> = emptyList(),
+ isOptional: Boolean = false
+) {
+ val descriptor = serializer<T>().descriptor
+ element(elementName, descriptor, annotations, isOptional)
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal class SerialDescriptorImpl(
+ override val serialName: String,
+ override val kind: SerialKind,
+ override val elementsCount: Int,
+ typeParameters: List<SerialDescriptor>,
+ builder: ClassSerialDescriptorBuilder
+) : SerialDescriptor, CachedNames {
+
+ override val annotations: List<Annotation> = builder.annotations
+ override val serialNames: Set<String> = builder.elementNames.toHashSet()
+
+ private val elementNames: Array<String> = builder.elementNames.toTypedArray()
+ private val elementDescriptors: Array<SerialDescriptor> = builder.elementDescriptors.compactArray()
+ private val elementAnnotations: Array<List<Annotation>> = builder.elementAnnotations.toTypedArray()
+ private val elementOptionality: BooleanArray = builder.elementOptionality.toBooleanArray()
+ private val name2Index: Map<String, Int> = elementNames.withIndex().map { it.value to it.index }.toMap()
+ private val typeParametersDescriptors: Array<SerialDescriptor> = typeParameters.compactArray()
+ private val _hashCode: Int by lazy { hashCodeImpl(typeParametersDescriptors) }
+
+ override fun getElementName(index: Int): String = elementNames.getChecked(index)
+ override fun getElementIndex(name: String): Int = name2Index[name] ?: CompositeDecoder.UNKNOWN_NAME
+ override fun getElementAnnotations(index: Int): List<Annotation> = elementAnnotations.getChecked(index)
+ override fun getElementDescriptor(index: Int): SerialDescriptor = elementDescriptors.getChecked(index)
+ override fun isElementOptional(index: Int): Boolean = elementOptionality.getChecked(index)
+
+ override fun equals(other: Any?): Boolean =
+ equalsImpl(other) { otherDescriptor: SerialDescriptorImpl ->
+ typeParametersDescriptors.contentEquals(
+ otherDescriptor.typeParametersDescriptors
+ )
+ }
+
+ override fun hashCode(): Int = _hashCode
+
+ override fun toString(): String {
+ return (0 until elementsCount).joinToString(", ", prefix = "$serialName(", postfix = ")") {
+ getElementName(it) + ": " + getElementDescriptor(it).serialName
+ }
+ }
+}
+
diff --git a/core/commonMain/src/kotlinx/serialization/descriptors/SerialKinds.kt b/core/commonMain/src/kotlinx/serialization/descriptors/SerialKinds.kt
new file mode 100644
index 00000000..5f7881aa
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/descriptors/SerialKinds.kt
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.descriptors
+
+import kotlinx.serialization.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+/**
+ * Serial kind is an intrinsic property of [SerialDescriptor] that indicates how
+ * the corresponding type is structurally represented by its serializer.
+ *
+ * Kind is used by serialization formats to determine how exactly the given type
+ * should be serialized. For example, JSON format detects the kind of the value and,
+ * depending on that, may write it as a plain value for primitive kinds, open a
+ * curly brace '{' for class-like structures and square bracket '[' for list- and array- like structures.
+ *
+ * Kinds are used both during serialization, to serialize a value properly and statically, and
+ * to introspect the type structure or build serialization schema.
+ *
+ * Kind should match the structure of the serialized form, not the structure of the corresponding Kotlin class.
+ * Meaning that if serializable class `class IntPair(val left: Int, val right: Int)` is represented by the serializer
+ * as a single `Long` value, its descriptor should have [PrimitiveKind.LONG] without nested elements even though the class itself
+ * represents a structure with two primitive fields.
+ */
+@ExperimentalSerializationApi
+public sealed class SerialKind {
+
+ /**
+ * Represents a Kotlin [Enum] with statically known values.
+ * All enum values should be enumerated in descriptor elements.
+ * Each element descriptor of a [Enum] kind represents an instance of a particular enum
+ * and has an [StructureKind.OBJECT] kind.
+ * Each [positional name][SerialDescriptor.getElementName] contains a corresponding enum element [name][Enum.name].
+ *
+ * Corresponding encoder and decoder methods are [Encoder.encodeEnum] and [Decoder.decodeEnum].
+ */
+ @ExperimentalSerializationApi
+ public object ENUM : SerialKind()
+
+ /**
+ * Represents an "unknown" type that will be known only at the moment of the serialization.
+ * Effectively it defers the choice of the serializer to a moment of the serialization, and can
+ * be used for [contextual][Contextual] serialization.
+ *
+ * To introspect descriptor of this kind, an instance of [SerializersModule] is required.
+ * See [capturedKClass] extension property for more details.
+ * However, if possible options are known statically (e.g. for sealed classes), they can be
+ * enumerated in child descriptors similarly to [ENUM].
+ */
+ @ExperimentalSerializationApi
+ public object CONTEXTUAL : SerialKind()
+
+ override fun toString(): String {
+ // KNPE should never happen, because SerialKind is sealed and all inheritors are non-anonymous
+ return this::class.simpleName!!
+ }
+
+ // Provide a stable hashcode for objects
+ override fun hashCode(): Int = toString().hashCode()
+}
+
+/**
+ * Values of primitive kinds usually are represented as a single value.
+ * All default serializers for Kotlin [primitives types](https://kotlinlang.org/docs/tutorials/kotlin-for-py/primitive-data-types-and-their-limitations.html)
+ * and [String] have primitive kind.
+ *
+ * ### Serializers interaction
+ *
+ * Serialization formats typically handle these kinds by calling a corresponding primitive method on encoder or decoder.
+ * For example, if the following serializable class `class Color(val red: Byte, val green: Byte, val blue: Byte)` is represented by your serializer
+ * as a single [Int] value, a typical serializer will serialize its value in the following manner:
+ * ```
+ * val intValue = color.rgbToInt()
+ * encoder.encodeInt(intValue)
+ * ```
+ * and a corresponding [Decoder] counterpart.
+ *
+ * ### Implementation note
+ *
+ * Serial descriptors for primitive kinds are not expected to have any nested elements, thus its element count should be zero.
+ * If a class is represented as a primitive value, its corresponding serial name *should not* be equal to the corresponding primitive type name.
+ * For the `Color` example, represented as single [Int], its descriptor should have [INT] kind, zero elements and serial name **not equals**
+ * to `kotlin.Int`: `PrimitiveDescriptor("my.package.ColorAsInt", PrimitiveKind.INT)`
+ */
+@OptIn(ExperimentalSerializationApi::class) // May be @Experimental, but break clients + makes impossible to use stable PrimitiveSerialDescriptor
+public sealed class PrimitiveKind : SerialKind() {
+ /**
+ * Primitive kind that represents a boolean `true`/`false` value.
+ * Corresponding Kotlin primitive is [Boolean].
+ * Corresponding encoder and decoder methods are [Encoder.encodeBoolean] and [Decoder.decodeBoolean].
+ */
+ public object BOOLEAN : PrimitiveKind()
+
+ /**
+ * Primitive kind that represents a single byte value.
+ * Corresponding Kotlin primitive is [Byte].
+ * Corresponding encoder and decoder methods are [Encoder.encodeByte] and [Decoder.decodeByte].
+ */
+ public object BYTE : PrimitiveKind()
+
+ /**
+ * Primitive kind that represents a 16-bit unicode character value.
+ * Corresponding Kotlin primitive is [Char].
+ * Corresponding encoder and decoder methods are [Encoder.encodeChar] and [Decoder.decodeChar].
+ */
+ public object CHAR : PrimitiveKind()
+
+ /**
+ * Primitive kind that represents a 16-bit short value.
+ * Corresponding Kotlin primitive is [Short].
+ * Corresponding encoder and decoder methods are [Encoder.encodeShort] and [Decoder.decodeShort].
+ */
+ public object SHORT : PrimitiveKind()
+
+ /**
+ * Primitive kind that represents a 32-bit int value.
+ * Corresponding Kotlin primitive is [Int].
+ * Corresponding encoder and decoder methods are [Encoder.encodeInt] and [Decoder.decodeInt].
+ */
+ public object INT : PrimitiveKind()
+
+ /**
+ * Primitive kind that represents a 64-bit long value.
+ * Corresponding Kotlin primitive is [Long].
+ * Corresponding encoder and decoder methods are [Encoder.encodeLong] and [Decoder.decodeLong].
+ */
+ public object LONG : PrimitiveKind()
+
+ /**
+ * Primitive kind that represents a 32-bit IEEE 754 floating point value.
+ * Corresponding Kotlin primitive is [Float].
+ * Corresponding encoder and decoder methods are [Encoder.encodeFloat] and [Decoder.decodeFloat].
+ */
+ public object FLOAT : PrimitiveKind()
+
+ /**
+ * Primitive kind that represents a 64-bit IEEE 754 floating point value.
+ * Corresponding Kotlin primitive is [Double].
+ * Corresponding encoder and decoder methods are [Encoder.encodeDouble] and [Decoder.decodeDouble].
+ */
+ public object DOUBLE : PrimitiveKind()
+
+ /**
+ * Primitive kind that represents a string value.
+ * Corresponding Kotlin primitive is [String].
+ * Corresponding encoder and decoder methods are [Encoder.encodeString] and [Decoder.decodeString].
+ */
+ public object STRING : PrimitiveKind()
+}
+
+/**
+ * Structure kind represents values with composite structure of nested elements of depth and arbitrary number.
+ * We acknowledge following structured kinds:
+ *
+ * ### Regular classes
+ * The most common case for serialization, that represents an arbitrary structure with fixed count of elements.
+ * When the regular Kotlin class is marked as [Serializable], its descriptor kind will be [CLASS].
+ *
+ * ### Lists
+ * [LIST] represent a structure with potentially unknown in advance number of elements of the same type.
+ * All standard serializable [List] implementors and arrays are represented as [LIST] kind of the same type.
+ *
+ * ### Maps
+ * [MAP] represent a structure with potentially unknown in advance number of key-value pairs of the same type.
+ * All standard serializable [Map] implementors are represented as [Map] kind of the same type.
+ *
+ * ### Kotlin objects
+ * A singleton object defined with `object` keyword with an [OBJECT] kind.
+ * By default, objects are serialized as empty structures without any states and their identity is preserved
+ * across serialization within the same process, so you always have the same instance of the object.
+ *
+ * ### Serializers interaction
+ * Serialization formats typically handle these kinds by marking structure start and end.
+ * E.g. the following serializable class `class IntHolder(myValue: Int)` of structure kind [CLASS] is handled by
+ * serializer as the following call sequence:
+ * ```
+ * val composite = encoder.beginStructure(descriptor) // Denotes the start of the structure
+ * composite.encodeIntElement(descriptor, index = 0, holder.myValue)
+ * composite.endStructure(descriptor) // Denotes the end of the structure
+ * ```
+ * and its corresponding [Decoder] counterpart.
+ *
+ * ### Serial descriptor implementors note
+ * These kinds can be used not only for collection and regular classes.
+ * For example, provided serializer for [Map.Entry] represents it as [Map] type, so it is serialized
+ * as `{"actualKey": "actualValue"}` map directly instead of `{"key": "actualKey", "value": "actualValue"}`
+ */
+@ExperimentalSerializationApi
+public sealed class StructureKind : SerialKind() {
+
+ /**
+ * Structure kind for regular classes with an arbitrary, but known statically, structure.
+ * Serializers typically encode classes with calls to [Encoder.beginStructure] and [CompositeEncoder.endStructure],
+ * writing the elements of the class between these calls.
+ */
+ public object CLASS : StructureKind()
+
+ /**
+ * Structure kind for lists and arrays of an arbitrary length.
+ * Serializers typically encode classes with calls to [Encoder.beginCollection] and [CompositeEncoder.endStructure],
+ * writing the elements of the list between these calls.
+ * Built-in list serializers treat elements as homogeneous, though application-specific serializers may impose
+ * application-specific restrictions on specific [LIST] types.
+ *
+ * Example of such application-specific serialization may be class `class ListOfThreeElements() : List<Any>`,
+ * for which an author of the serializer knows that while it is `List<Any>`, in fact, is always has three elements
+ * of a known type (e.g. the first is always a string, the second one is always an int etc.)
+ */
+ public object LIST : StructureKind()
+
+ /**
+ * Structure kind for maps of an arbitrary length.
+ * Serializers typically encode classes with calls to [Encoder.beginCollection] and [CompositeEncoder.endStructure],
+ * writing the elements of the map between these calls.
+ *
+ * Built-in map serializers treat elements as homogeneous, though application-specific serializers may impose
+ * application-specific restrictions on specific [MAP] types.
+ */
+ public object MAP : StructureKind()
+
+ /**
+ * Structure kind for singleton objects defined with `object` keyword.
+ * By default, objects are serialized as empty structures without any state and their identity is preserved
+ * across serialization within the same process, so you always have the same instance of the object.
+ *
+ * Empty structure is represented as a call to [Encoder.beginStructure] with the following [CompositeEncoder.endStructure]
+ * without any intermediate encodings.
+ */
+ public object OBJECT : StructureKind()
+}
+
+/**
+ * Polymorphic kind represents a (bounded) polymorphic value, that is referred
+ * by some base class or interface, but its structure is defined by one of the possible implementations.
+ * Polymorphic kind is, by its definition, a union kind and is extracted to its own subtype to emphasize
+ * bounded and sealed polymorphism common property: not knowing the actual type statically and requiring
+ * formats to additionally encode it.
+ */
+@ExperimentalSerializationApi
+public sealed class PolymorphicKind : SerialKind() {
+ /**
+ * Sealed kind represents Kotlin sealed classes, where all subclasses are known statically at the moment of declaration.
+ * [SealedClassSerializer] can be used as an example of sealed serialization.
+ */
+ public object SEALED : PolymorphicKind()
+
+ /**
+ * Open polymorphic kind represents statically unknown type that is hidden behind a given base class or interface.
+ * [PolymorphicSerializer] can be used as an example of polymorphic serialization.
+ *
+ * Due to security concerns and typical mistakes that arises from polymorphic serialization, by default
+ * `kotlinx.serialization` provides only bounded polymorphic serialization, forcing users to register all possible
+ * serializers for a given base class or interface.
+ *
+ * To introspect descriptor of this kind (e.g. list possible subclasses), an instance of [SerializersModule] is required.
+ * See [capturedKClass] extension property for more details.
+ */
+ public object OPEN : PolymorphicKind()
+}
diff --git a/core/commonMain/src/kotlinx/serialization/encoding/AbstractDecoder.kt b/core/commonMain/src/kotlinx/serialization/encoding/AbstractDecoder.kt
new file mode 100644
index 00000000..8e2799c9
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/encoding/AbstractDecoder.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.encoding
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+
+/**
+ * A skeleton implementation of both [Decoder] and [CompositeDecoder] that can be used
+ * for simple formats and for testability purpose.
+ * Most of the `decode*` methods have default implementation that delegates `decodeValue(value: Any) as TargetType`.
+ * See [Decoder] documentation for information about each particular `decode*` method.
+ */
+@ExperimentalSerializationApi
+public abstract class AbstractDecoder : Decoder, CompositeDecoder {
+
+ /**
+ * Invoked to decode a value when specialized `decode*` method was not overridden.
+ */
+ public open fun decodeValue(): Any = throw SerializationException("${this::class} can't retrieve untyped values")
+
+ override fun decodeNotNullMark(): Boolean = true
+ override fun decodeNull(): Nothing? = null
+ override fun decodeBoolean(): Boolean = decodeValue() as Boolean
+ override fun decodeByte(): Byte = decodeValue() as Byte
+ override fun decodeShort(): Short = decodeValue() as Short
+ override fun decodeInt(): Int = decodeValue() as Int
+ override fun decodeLong(): Long = decodeValue() as Long
+ override fun decodeFloat(): Float = decodeValue() as Float
+ override fun decodeDouble(): Double = decodeValue() as Double
+ override fun decodeChar(): Char = decodeValue() as Char
+ override fun decodeString(): String = decodeValue() as String
+ override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeValue() as Int
+
+ override fun decodeInline(inlineDescriptor: SerialDescriptor): Decoder = this
+
+ // overwrite by default
+ public open fun <T : Any?> decodeSerializableValue(
+ deserializer: DeserializationStrategy<T>,
+ previousValue: T? = null
+ ): T = decodeSerializableValue(deserializer)
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder = this
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ }
+
+ final override fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean = decodeBoolean()
+ final override fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte = decodeByte()
+ final override fun decodeShortElement(descriptor: SerialDescriptor, index: Int): Short = decodeShort()
+ final override fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int = decodeInt()
+ final override fun decodeLongElement(descriptor: SerialDescriptor, index: Int): Long = decodeLong()
+ final override fun decodeFloatElement(descriptor: SerialDescriptor, index: Int): Float = decodeFloat()
+ final override fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int): Double = decodeDouble()
+ final override fun decodeCharElement(descriptor: SerialDescriptor, index: Int): Char = decodeChar()
+ final override fun decodeStringElement(descriptor: SerialDescriptor, index: Int): String = decodeString()
+
+ override fun decodeInlineElement(
+ descriptor: SerialDescriptor,
+ index: Int
+ ): Decoder = decodeInline(descriptor.getElementDescriptor(index))
+
+ override fun <T> decodeSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ deserializer: DeserializationStrategy<T>,
+ previousValue: T?
+ ): T = decodeSerializableValue(deserializer, previousValue)
+
+ final override fun <T : Any> decodeNullableSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ deserializer: DeserializationStrategy<T?>,
+ previousValue: T?
+ ): T? {
+ val isNullabilitySupported = deserializer.descriptor.isNullable
+ return if (isNullabilitySupported || decodeNotNullMark()) decodeSerializableValue(deserializer, previousValue) else decodeNull()
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/encoding/AbstractEncoder.kt b/core/commonMain/src/kotlinx/serialization/encoding/AbstractEncoder.kt
new file mode 100644
index 00000000..f197c40f
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/encoding/AbstractEncoder.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.encoding
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.internal.*
+
+/**
+ * A skeleton implementation of both [Encoder] and [CompositeEncoder] that can be used
+ * for simple formats and for testability purpose.
+ * Most of the `encode*` methods have default implementation that delegates `encodeValue(value: Any)`.
+ * See [Encoder] documentation for information about each particular `encode*` method.
+ */
+@ExperimentalSerializationApi
+public abstract class AbstractEncoder : Encoder, CompositeEncoder {
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder = this
+
+ override fun endStructure(descriptor: SerialDescriptor) {}
+
+ /**
+ * Invoked before writing an element that is part of the structure to determine whether it should be encoded.
+ * Element information can be obtained from the [descriptor] by the given [index].
+ *
+ * @return `true` if the value should be encoded, false otherwise
+ */
+ public open fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean = true
+
+ /**
+ * Invoked to encode a value when specialized `encode*` method was not overridden.
+ */
+ public open fun encodeValue(value: Any): Unit =
+ throw SerializationException("Non-serializable ${value::class} is not supported by ${this::class} encoder")
+
+ override fun encodeNull() {
+ throw SerializationException("'null' is not supported by default")
+ }
+
+ override fun encodeBoolean(value: Boolean): Unit = encodeValue(value)
+ override fun encodeByte(value: Byte): Unit = encodeValue(value)
+ override fun encodeShort(value: Short): Unit = encodeValue(value)
+ override fun encodeInt(value: Int): Unit = encodeValue(value)
+ override fun encodeLong(value: Long): Unit = encodeValue(value)
+ override fun encodeFloat(value: Float): Unit = encodeValue(value)
+ override fun encodeDouble(value: Double): Unit = encodeValue(value)
+ override fun encodeChar(value: Char): Unit = encodeValue(value)
+ override fun encodeString(value: String): Unit = encodeValue(value)
+ override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int): Unit = encodeValue(index)
+
+ override fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder = this
+
+ // Delegating implementation of CompositeEncoder
+ final override fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean) { if (encodeElement(descriptor, index)) encodeBoolean(value) }
+ final override fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte) { if (encodeElement(descriptor, index)) encodeByte(value) }
+ final override fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short) { if (encodeElement(descriptor, index)) encodeShort(value) }
+ final override fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int) { if (encodeElement(descriptor, index)) encodeInt(value) }
+ final override fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long) { if (encodeElement(descriptor, index)) encodeLong(value) }
+ final override fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float) { if (encodeElement(descriptor, index)) encodeFloat(value) }
+ final override fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double) { if (encodeElement(descriptor, index)) encodeDouble(value) }
+ final override fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char) { if (encodeElement(descriptor, index)) encodeChar(value) }
+ final override fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String) { if (encodeElement(descriptor, index)) encodeString(value) }
+
+ final override fun encodeInlineElement(
+ descriptor: SerialDescriptor,
+ index: Int
+ ): Encoder =
+ if (encodeElement(descriptor, index)) encodeInline(descriptor.getElementDescriptor(index)) else NoOpEncoder
+
+ override fun <T : Any?> encodeSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ serializer: SerializationStrategy<T>,
+ value: T
+ ) {
+ if (encodeElement(descriptor, index))
+ encodeSerializableValue(serializer, value)
+ }
+
+ override fun <T : Any> encodeNullableSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ serializer: SerializationStrategy<T>,
+ value: T?
+ ) {
+ if (encodeElement(descriptor, index))
+ encodeNullableSerializableValue(serializer, value)
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt b/core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt
new file mode 100644
index 00000000..3e93e3db
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt
@@ -0,0 +1,576 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.encoding
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.modules.*
+
+/**
+ * Decoder is a core deserialization primitive that encapsulates the knowledge of the underlying
+ * format and an underlying storage, exposing only structural methods to the deserializer, making it completely
+ * format-agnostic. Deserialization process takes a decoder and asks him for a sequence of primitive elements,
+ * defined by a deserializer serial form, while decoder knows how to retrieve these primitive elements from an actual format
+ * representations.
+ *
+ * Decoder provides high-level API that operates with basic primitive types, collections
+ * and nested structures. Internally, the decoder represents input storage, and operates with its state
+ * and lower level format-specific details.
+ *
+ * To be more specific, serialization asks a decoder for a sequence of "give me an int, give me
+ * a double, give me a list of strings and give me another object that is a nested int", while decoding
+ * transforms this sequence into a format-specific commands such as "parse the part of the string until the next quotation mark
+ * as an int to retrieve an int, parse everything within the next curly braces to retrieve elements of a nested object etc."
+ *
+ * The symmetric interface for the serialization process is [Encoder].
+ *
+ * ### Deserialization. Primitives
+ *
+ * If a class is represented as a single [primitive][PrimitiveKind] value in its serialized form,
+ * then one of the `decode*` methods (e.g. [decodeInt]) can be used directly.
+ *
+ * ### Deserialization. Structured types
+ *
+ * If a class is represented as a structure or has multiple values in its serialized form,
+ * `decode*` methods are not that helpful, because format may not require a strict order of data
+ * (e.g. JSON or XML), do not allow working with collection types or establish structure boundaries.
+ * All these capabilities are delegated to the [CompositeDecoder] interface with a more specific API surface.
+ * To denote a structure start, [beginStructure] should be used.
+ * ```
+ * // Denote the structure start,
+ * val composite = decoder.beginStructure(descriptor)
+ * // Decode all elements within the structure using 'composite'
+ * ...
+ * // Denote the structure end
+ * composite.endStructure(descriptor)
+ * ```
+ *
+ * E.g. if the decoder belongs to JSON format, then [beginStructure] will parse an opening bracket
+ * (`{` or `[`, depending on the descriptor kind), returning the [CompositeDecoder] that is aware of colon separator,
+ * that should be read after each key-value pair, whilst [CompositeDecoder.endStructure] will parse a closing bracket.
+ *
+ * ### Exception guarantees.
+ * For the regular exceptions, such as invalid input, missing control symbols or attributes and unknown symbols,
+ * [SerializationException] can be thrown by any decoder methods. It is recommended to declare a format-specific
+ * subclass of [SerializationException] and throw it.
+ *
+ * ### Format encapsulation
+ *
+ * For example, for the following deserializer:
+ * ```
+ * class StringHolder(val stringValue: String)
+ *
+ * object StringPairDeserializer : DeserializationStrategy<StringHolder> {
+ * override val descriptor = ...
+ *
+ * override fun deserializer(decoder: Decoder): StringHolder {
+ * // Denotes start of the structure, StringHolder is not a "plain" data type
+ * val composite = decoder.beginStructure(descriptor)
+ * if (composite.decodeElementIndex(descriptor) != 0)
+ * throw MissingFieldException("Field 'stringValue' is missing")
+ * // Decode the nested string value
+ * val value = composite.decodeStringElement(descriptor, index = 0)
+ * // Denotes end of the structure
+ * composite.endStructure(descriptor)
+ * }
+ * }
+ * ```
+ *
+ * ### Exception safety
+ *
+ * In general, catching [SerializationException] from any of `decode*` methods is not allowed and produces unspecified behaviour.
+ * After thrown exception, current decoder is left in an arbitrary state, no longer suitable for further decoding.
+ *
+ * This deserializer does not know anything about the underlying data and will work with any properly-implemented decoder.
+ * JSON, for example, parses an opening bracket `{` during the `beginStructure` call, checks that the next key
+ * after this bracket is `stringValue` (using the descriptor), returns the value after the colon as string value
+ * and parses closing bracket `}` during the `endStructure`.
+ * XML would do roughly the same, but with different separators and parsing structures, while ProtoBuf
+ * machinery could be completely different.
+ * In any case, all these parsing details are encapsulated by a decoder.
+ *
+ * ### Decoder implementation
+ *
+ * While being strictly typed, an underlying format can transform actual types in the way it wants.
+ * For example, a format can support only string types and encode/decode all primitives in a string form:
+ * ```
+ * StringFormatDecoder : Decoder {
+ *
+ * ...
+ * override fun decodeDouble(): Double = decodeString().toDouble()
+ * override fun decodeInt(): Int = decodeString().toInt()
+ * ...
+ * }
+ * ```
+ *
+ * ### Not stable for inheritance
+ *
+ * `Decoder` interface is not stable for inheritance in 3rd party libraries, as new methods
+ * might be added to this interface or contracts of the existing methods can be changed.
+ */
+public interface Decoder {
+ /**
+ * Context of the current serialization process, including contextual and polymorphic serialization and,
+ * potentially, a format-specific configuration.
+ */
+ public val serializersModule: SerializersModule
+
+ /**
+ * Returns `true` if the current value in decoder is not null, false otherwise.
+ * This method is usually used to decode potentially nullable data:
+ * ```
+ * // Could be String? deserialize() method
+ * public fun deserialize(decoder: Decoder): String? {
+ * if (decoder.decodeNotNullMark()) {
+ * return decoder.decodeString()
+ * } else {
+ * return decoder.decodeNull()
+ * }
+ * }
+ * ```
+ */
+ @ExperimentalSerializationApi
+ public fun decodeNotNullMark(): Boolean
+
+ /**
+ * Decodes the `null` value and returns it.
+ *
+ * It is expected that `decodeNotNullMark` was called
+ * prior to `decodeNull` invocation and the case when it returned `true` was handled.
+ */
+ @ExperimentalSerializationApi
+ public fun decodeNull(): Nothing?
+
+ /**
+ * Decodes a boolean value.
+ * Corresponding kind is [PrimitiveKind.BOOLEAN].
+ */
+ public fun decodeBoolean(): Boolean
+
+ /**
+ * Decodes a single byte value.
+ * Corresponding kind is [PrimitiveKind.BYTE].
+ */
+ public fun decodeByte(): Byte
+
+ /**
+ * Decodes a 16-bit short value.
+ * Corresponding kind is [PrimitiveKind.SHORT].
+ */
+ public fun decodeShort(): Short
+
+ /**
+ * Decodes a 16-bit unicode character value.
+ * Corresponding kind is [PrimitiveKind.CHAR].
+ */
+ public fun decodeChar(): Char
+
+ /**
+ * Decodes a 32-bit integer value.
+ * Corresponding kind is [PrimitiveKind.INT].
+ */
+ public fun decodeInt(): Int
+
+ /**
+ * Decodes a 64-bit integer value.
+ * Corresponding kind is [PrimitiveKind.LONG].
+ */
+ public fun decodeLong(): Long
+
+ /**
+ * Decodes a 32-bit IEEE 754 floating point value.
+ * Corresponding kind is [PrimitiveKind.FLOAT].
+ */
+ public fun decodeFloat(): Float
+
+ /**
+ * Decodes a 64-bit IEEE 754 floating point value.
+ * Corresponding kind is [PrimitiveKind.DOUBLE].
+ */
+ public fun decodeDouble(): Double
+
+ /**
+ * Decodes a string value.
+ * Corresponding kind is [PrimitiveKind.STRING].
+ */
+ public fun decodeString(): String
+
+ /**
+ * Decodes a enum value and returns its index in [enumDescriptor] elements collection.
+ * Corresponding kind is [SerialKind.ENUM].
+ *
+ * E.g. for the enum `enum class Letters { A, B, C, D }` and
+ * underlying input "C", [decodeEnum] method should return `2` as a result.
+ *
+ * This method does not imply any restrictions on the input format,
+ * the format is free to store the enum by its name, index, ordinal or any other enum representation.
+ */
+ public fun decodeEnum(enumDescriptor: SerialDescriptor): Int
+
+ /**
+ * Returns [Decoder] for decoding an underlying type of an inline class.
+ * [inlineDescriptor] describes a target inline class.
+ *
+ * Namely, for the `@Serializable inline class MyInt(val my: Int)`,
+ * the following sequence is used:
+ * ```
+ * thisDecoder.decodeInline(MyInt.serializer().descriptor).decodeInt()
+ * ```
+ *
+ * Current decoder may return any other instance of [Decoder] class,
+ * depending on the provided [inlineDescriptor].
+ * For example, when this function is called on Json decoder with
+ * `UInt.serializer().descriptor`, the returned decoder is able
+ * to decode unsigned integers.
+ *
+ * Note that this function returns [Decoder] instead of the [CompositeDecoder]
+ * because inline classes always have the single property.
+ * Calling [Decoder.beginStructure] on returned instance leads to an undefined behavior.
+ */
+ @ExperimentalSerializationApi
+ public fun decodeInline(inlineDescriptor: SerialDescriptor): Decoder
+
+ /**
+ * Decodes the beginning of the nested structure in a serialized form
+ * and returns [CompositeDecoder] responsible for decoding this very structure.
+ *
+ * Typically, classes, collections and maps are represented as a nested structure in a serialized form.
+ * E.g. the following JSON
+ * ```
+ * {
+ * "a": 2,
+ * "b": { "nested": "c" }
+ * "c": [1, 2, 3],
+ * "d": null
+ * }
+ * ```
+ * has three nested structures: the very beginning of the data, "b" value and "c" value.
+ */
+ public fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder
+
+ /**
+ * Decodes the value of type [T] by delegating the decoding process to the given [deserializer].
+ * For example, `decodeInt` call us equivalent to delegating integer decoding to [Int.serializer][Int.Companion.serializer]:
+ * `decodeSerializableValue(IntSerializer)`
+ */
+ public fun <T : Any?> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T =
+ deserializer.deserialize(this)
+
+ /**
+ * Decodes the nullable value of type [T] by delegating the decoding process to the given [deserializer].
+ */
+ @ExperimentalSerializationApi
+ public fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T?>): T? {
+ val isNullabilitySupported = deserializer.descriptor.isNullable
+ return if (isNullabilitySupported || decodeNotNullMark()) decodeSerializableValue(deserializer) else decodeNull()
+ }
+}
+
+/**
+ * [CompositeDecoder] is a part of decoding process that is bound to a particular structured part of
+ * the serialized form, described by the serial descriptor passed to [Decoder.beginStructure].
+ *
+ * Typically, for unordered data, [CompositeDecoder] is used by a serializer withing a [decodeElementIndex]-based
+ * loop that decodes all the required data one-by-one in any order and then terminates by calling [endStructure].
+ * Please refer to [decodeElementIndex] for example of such loop.
+ *
+ * All `decode*` methods have `index` and `serialDescriptor` parameters with a strict semantics and constraints:
+ * * `descriptor` argument is always the same as one used in [Decoder.beginStructure].
+ * * `index` of the element being decoded. For [sequential][decodeSequentially] decoding, it is always a monotonic
+ * sequence from `0` to `descriptor.elementsCount` and for indexing-loop it is always an index that [decodeElementIndex]
+ * has returned from the last call.
+ *
+ * The symmetric interface for the serialization process is [CompositeEncoder].
+ *
+ * ### Not stable for inheritance
+ *
+ * `CompositeDecoder` interface is not stable for inheritance in 3rd party libraries, as new methods
+ * might be added to this interface or contracts of the existing methods can be changed.
+ */
+public interface CompositeDecoder {
+
+ /**
+ * Results of [decodeElementIndex] used for decoding control flow.
+ */
+ public companion object {
+ /**
+ * Value returned by [decodeElementIndex] when the underlying input has no more data in the current structure.
+ * When this value is returned, no methods of the decoder should be called but [endStructure].
+ */
+ public const val DECODE_DONE: Int = -1
+
+ /**
+ * Value returned by [decodeElementIndex] when the format encountered an unknown element
+ * (expected neither by the structure of serial descriptor, nor by the format itself).
+ */
+ public const val UNKNOWN_NAME: Int = -3
+ }
+
+ /**
+ * Context of the current decoding process, including contextual and polymorphic serialization and,
+ * potentially, a format-specific configuration.
+ */
+ public val serializersModule: SerializersModule
+
+ /**
+ * Denotes the end of the structure associated with current decoder.
+ * For example, composite decoder of JSON format will expect (and parse)
+ * a closing bracket in the underlying input.
+ */
+ public fun endStructure(descriptor: SerialDescriptor)
+
+ /**
+ * Checks whether the current decoder supports strictly ordered decoding of the data
+ * without calling to [decodeElementIndex].
+ * If the method returns `true`, the caller might skip [decodeElementIndex] calls
+ * and start invoking `decode*Element` directly, incrementing the index of the element one by one.
+ * This method can be called by serializers (either generated or user-defined) as a performance optimization,
+ * but there is no guarantee that the method will be ever called. Practically, it means that implementations
+ * that may benefit from sequential decoding should also support a regular [decodeElementIndex]-based decoding as well.
+ *
+ * Example of usage:
+ * ```
+ * class MyPair(i: Int, d: Double)
+ *
+ * object MyPairSerializer : KSerializer<MyPair> {
+ * // ... other methods omitted
+ *
+ * fun deserialize(decoder: Decoder): MyPair {
+ * val composite = decoder.beginStructure(descriptor)
+ * if (composite.decodeSequentially()) {
+ * val i = composite.decodeIntElement(descriptor, index = 0) // Mind the sequential indexing
+ * val d = composite.decodeIntElement(descriptor, index = 1)
+ * composite.endStructure(descriptor)
+ * return MyPair(i, d)
+ * } else {
+ * // Fallback to `decodeElementIndex` loop, refer to its documentation for details
+ * }
+ * }
+ * }
+ * ```
+ * This example is a rough equivalent of what serialization plugin generates for serializable pair class.
+ *
+ * Sequential decoding is a performance optimization for formats with strictly ordered schema,
+ * usually binary ones. Regular formats such as JSON or ProtoBuf cannot use this optimization,
+ * because e.g. in the latter example, the same data can be represented both as
+ * `{"i": 1, "d": 1.0}`"` and `{"d": 1.0, "i": 1}` (thus, unordered).
+ */
+ @ExperimentalSerializationApi
+ public fun decodeSequentially(): Boolean = false
+
+ /**
+ * Decodes the index of the next element to be decoded.
+ * Index represents a position of the current element in the serial descriptor element that can be found
+ * with [SerialDescriptor.getElementIndex].
+ *
+ * If this method returns non-negative index, the caller should call one of the `decode*Element` methods
+ * with a resulting index.
+ * Apart from positive values, this method can return [DECODE_DONE] to indicate that no more elements
+ * are left or [UNKNOWN_NAME] to indicate that symbol with an unknown name was encountered.
+ *
+ * Example of usage:
+ * ```
+ * class MyPair(i: Int, d: Double)
+ *
+ * object MyPairSerializer : KSerializer<MyPair> {
+ * // ... other methods omitted
+ *
+ * fun deserialize(decoder: Decoder): MyPair {
+ * val composite = decoder.beginStructure(descriptor)
+ * var i: Int? = null
+ * var d: Double? = null
+ * while (true) {
+ * when (val index = composite.decodeElementIndex(descriptor)) {
+ * 0 -> i = composite.decodeIntElement(descriptor, 0)
+ * 1 -> d = composite.decodeDoubleElement(descriptor, 1)
+ * DECODE_DONE -> break // Input is over
+ * else -> error("Unexpected index: $index)
+ * }
+ * }
+ * composite.endStructure(descriptor)
+ * require(i != null && d != null)
+ * return MyPair(i, d)
+ * }
+ * }
+ * ```
+ * This example is a rough equivalent of what serialization plugin generates for serializable pair class.
+ *
+ * The need in such a loop comes from unstructured nature of most serialization formats.
+ * For example, JSON for the following input `{"d": 2.0, "i": 1}`, will first read `d` key with index `1`
+ * and only after `i` with the index `0`.
+ *
+ * A potential implementation of this method for JSON format can be the following:
+ * ```
+ * fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ * // Ignore arrays
+ * val nextKey: String? = myStringJsonParser.nextKey()
+ * if (nextKey == null) return DECODE_DONE
+ * return descriptor.getElementIndex(nextKey) // getElementIndex can return UNKNOWN_NAME
+ * }
+ * ```
+ *
+ * If [decodeSequentially] returns `true`, the caller might skip calling this method.
+ */
+ public fun decodeElementIndex(descriptor: SerialDescriptor): Int
+
+ /**
+ * Method to decode collection size that may be called before the collection decoding.
+ * Collection type includes [Collection], [Map] and [Array] (including primitive arrays).
+ * Method can return `-1` if the size is not known in advance, though for [sequential decoding][decodeSequentially]
+ * knowing precise size is a mandatory requirement.
+ */
+ public fun decodeCollectionSize(descriptor: SerialDescriptor): Int = -1
+
+ /**
+ * Decodes a boolean value from the underlying input.
+ * The resulting value is associated with the [descriptor] element at the given [index].
+ * The element at the given index should have [PrimitiveKind.BOOLEAN] kind.
+ */
+ public fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean
+
+ /**
+ * Decodes a single byte value from the underlying input.
+ * The resulting value is associated with the [descriptor] element at the given [index].
+ * The element at the given index should have [PrimitiveKind.BYTE] kind.
+ */
+ public fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte
+
+ /**
+ * Decodes a 16-bit unicode character value from the underlying input.
+ * The resulting value is associated with the [descriptor] element at the given [index].
+ * The element at the given index should have [PrimitiveKind.CHAR] kind.
+ */
+ public fun decodeCharElement(descriptor: SerialDescriptor, index: Int): Char
+
+ /**
+ * Decodes a 16-bit short value from the underlying input.
+ * The resulting value is associated with the [descriptor] element at the given [index].
+ * The element at the given index should have [PrimitiveKind.SHORT] kind.
+ */
+ public fun decodeShortElement(descriptor: SerialDescriptor, index: Int): Short
+
+ /**
+ * Decodes a 32-bit integer value from the underlying input.
+ * The resulting value is associated with the [descriptor] element at the given [index].
+ * The element at the given index should have [PrimitiveKind.INT] kind.
+ */
+ public fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int
+
+ /**
+ * Decodes a 64-bit integer value from the underlying input.
+ * The resulting value is associated with the [descriptor] element at the given [index].
+ * The element at the given index should have [PrimitiveKind.LONG] kind.
+ */
+ public fun decodeLongElement(descriptor: SerialDescriptor, index: Int): Long
+
+ /**
+ * Decodes a 32-bit IEEE 754 floating point value from the underlying input.
+ * The resulting value is associated with the [descriptor] element at the given [index].
+ * The element at the given index should have [PrimitiveKind.FLOAT] kind.
+ */
+ public fun decodeFloatElement(descriptor: SerialDescriptor, index: Int): Float
+
+ /**
+ * Decodes a 64-bit IEEE 754 floating point value from the underlying input.
+ * The resulting value is associated with the [descriptor] element at the given [index].
+ * The element at the given index should have [PrimitiveKind.DOUBLE] kind.
+ */
+ public fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int): Double
+
+ /**
+ * Decodes a string value from the underlying input.
+ * The resulting value is associated with the [descriptor] element at the given [index].
+ * The element at the given index should have [PrimitiveKind.STRING] kind.
+ */
+ public fun decodeStringElement(descriptor: SerialDescriptor, index: Int): String
+
+ /**
+ * Returns [Decoder] for decoding an underlying type of an inline class.
+ * Serializable inline class is described by the [child descriptor][SerialDescriptor.getElementDescriptor]
+ * of given [descriptor] at [index].
+ *
+ * Namely, for the `@Serializable inline class MyInt(val my: Int)`,
+ * and `@Serializable class MyData(val myInt: MyInt)`
+ * the following sequence is used:
+ * ```
+ * thisDecoder.decodeInlineElement(MyData.serializer().descriptor, 0).decodeInt()
+ * ```
+ *
+ * This method provides an opportunity for the optimization and its invocation should be identical to
+ * ```
+ * thisDecoder.decodeSerializableElement(MyData.serializer.descriptor, 0, MyInt.serializer())
+ * ```
+ *
+ * Current decoder may return any other instance of [Decoder] class, depending on the provided descriptor.
+ * For example, when this function is called on Json decoder with descriptor that has
+ * `UInt.serializer().descriptor` at the given [index], the returned decoder is able
+ * to decode unsigned integers.
+ *
+ * Note that this function returns [Decoder] instead of the [CompositeDecoder]
+ * because inline classes always have the single property.
+ * Calling [Decoder.beginStructure] on returned instance leads to an undefined behavior.
+ *
+ * @see Decoder.decodeInline
+ * @see SerialDescriptor.getElementDescriptor
+ */
+ @ExperimentalSerializationApi
+ public fun decodeInlineElement(
+ descriptor: SerialDescriptor,
+ index: Int
+ ): Decoder
+
+ /**
+ * Decodes value of the type [T] with the given [deserializer].
+ *
+ * Implementations of [CompositeDecoder] may use their format-specific deserializers
+ * for particular data types, e.g. handle [ByteArray] specifically if format is binary.
+ *
+ * If value at given [index] was already decoded with previous [decodeSerializableElement] call with the same index,
+ * [previousValue] would contain a previously decoded value.
+ * This parameter can be used to aggregate multiple values of the given property to the only one.
+ * Implementation can safely ignore it and return a new value, effectively using 'the last one wins' strategy,
+ * or apply format-specific aggregating strategies, e.g. appending scattered Protobuf lists to a single one.
+ */
+ public fun <T : Any?> decodeSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ deserializer: DeserializationStrategy<T>,
+ previousValue: T? = null
+ ): T
+
+ /**
+ * Decodes nullable value of the type [T] with the given [deserializer].
+ *
+ * If value at given [index] was already decoded with previous [decodeSerializableElement] call with the same index,
+ * [previousValue] would contain a previously decoded value.
+ * This parameter can be used to aggregate multiple values of the given property to the only one.
+ * Implementation can safely ignore it and return a new value, efficiently using 'the last one wins' strategy,
+ * or apply format-specific aggregating strategies, e.g. appending scattered Protobuf lists to a single one.
+ */
+ @ExperimentalSerializationApi
+ public fun <T : Any> decodeNullableSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ deserializer: DeserializationStrategy<T?>,
+ previousValue: T? = null
+ ): T?
+}
+
+/**
+ * Begins a structure, decodes it using the given [block], ends it and returns decoded element.
+ */
+public inline fun <T> Decoder.decodeStructure(
+ descriptor: SerialDescriptor,
+ crossinline block: CompositeDecoder.() -> T
+): T {
+ val composite = beginStructure(descriptor)
+ val result = composite.block()
+ composite.endStructure(descriptor)
+ return result
+}
+
+private const val decodeMethodDeprecated = "Please migrate to decodeElement method which accepts old value." +
+ "Feel free to ignore it if your format does not support updates."
diff --git a/core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt b/core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt
new file mode 100644
index 00000000..1113b1c7
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt
@@ -0,0 +1,511 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.encoding
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.modules.*
+
+/**
+ * Encoder is a core serialization primitive that encapsulates the knowledge of the underlying
+ * format and its storage, exposing only structural methods to the serializer, making it completely
+ * format-agnostic. Serialization process transforms a single value into the sequence of its
+ * primitive elements, also called its serial form, while encoding transforms these primitive elements into an actual
+ * format representation: JSON string, ProtoBuf ByteArray, in-memory map representation etc.
+ *
+ * Encoder provides high-level API that operates with basic primitive types, collections
+ * and nested structures. Internally, encoder represents output storage and operates with its state
+ * and lower level format-specific details.
+ *
+ * To be more specific, serialization transforms a value into a sequence of "here is an int, here is
+ * a double, here a list of strings and here is another object that is a nested int", while encoding
+ * transforms this sequence into a format-specific commands such as "insert opening curly bracket
+ * for a nested object start, insert a name of the value, and the value separated with colon for an int etc."
+ *
+ * The symmetric interface for the deserialization process is [Decoder].
+ *
+ * ### Serialization. Primitives
+ *
+ * If a class is represented as a single [primitive][PrimitiveKind] value in its serialized form,
+ * then one of the `encode*` methods (e.g. [encodeInt]) can be used directly.
+ *
+ * ### Serialization. Structured types.
+ *
+ * If a class is represented as a structure or has multiple values in its serialized form,
+ * `encode*` methods are not that helpful, because they do not allow working with collection types or establish structure boundaries.
+ * All these capabilities are delegated to the [CompositeEncoder] interface with a more specific API surface.
+ * To denote a structure start, [beginStructure] should be used.
+ * ```
+ * // Denote the structure start,
+ * val composite = encoder.beginStructure(descriptor)
+ * // Encoding all elements within the structure using 'composite'
+ * ...
+ * // Denote the structure end
+ * composite.endStructure(descriptor)
+ * ```
+ *
+ * E.g. if the encoder belongs to JSON format, then [beginStructure] will write an opening bracket
+ * (`{` or `[`, depending on the descriptor kind), returning the [CompositeEncoder] that is aware of colon separator,
+ * that should be appended between each key-value pair, whilst [CompositeEncoder.endStructure] will write a closing bracket.
+ *
+ * ### Exception guarantees.
+ * For the regular exceptions, such as invalid input, conflicting serial names,
+ * [SerializationException] can be thrown by any encoder methods.
+ * It is recommended to declare a format-specific subclass of [SerializationException] and throw it.
+ *
+ * ### Format encapsulation
+ *
+ * For example, for the following serializer:
+ * ```
+ * class StringHolder(val stringValue: String)
+ *
+ * object StringPairDeserializer : SerializationStrategy<StringHolder> {
+ * override val descriptor = ...
+ *
+ * override fun serializer(encoder: Encoder, value: StringHolder) {
+ * // Denotes start of the structure, StringHolder is not a "plain" data type
+ * val composite = encoder.beginStructure(descriptor)
+ * // Encode the nested string value
+ * composite.encodeStringElement(descriptor, index = 0)
+ * // Denotes end of the structure
+ * composite.endStructure(descriptor)
+ * }
+ * }
+ * ```
+ *
+ * This serializer does not know anything about the underlying storage and will work with any properly-implemented encoder.
+ * JSON, for example, writes an opening bracket `{` during the `beginStructure` call, writes 'stringValue` key along
+ * with its value in `encodeStringElement` and writes the closing bracket `}` during the `endStructure`.
+ * XML would do roughly the same, but with different separators and structures, while ProtoBuf
+ * machinery could be completely different.
+ * In any case, all these parsing details are encapsulated by an encoder.
+ *
+ * ### Exception safety
+ *
+ * In general, catching [SerializationException] from any of `encode*` methods is not allowed and produces unspecified behaviour.
+ * After thrown exception, current encoder is left in an arbitrary state, no longer suitable for further encoding.
+ *
+ * ### Encoder implementation.
+ *
+ * While being strictly typed, an underlying format can transform actual types in the way it wants.
+ * For example, a format can support only string types and encode/decode all primitives in a string form:
+ * ```
+ * StringFormatEncoder : Encoder {
+ *
+ * ...
+ * override fun encodeDouble(value: Double) = encodeString(value.toString())
+ * override fun encodeInt(value: Int) = encodeString(value.toString())
+ * ...
+ * }
+ * ```
+ *
+ * ### Not stable for inheritance
+ *
+ * `Encoder` interface is not stable for inheritance in 3rd party libraries, as new methods
+ * might be added to this interface or contracts of the existing methods can be changed.
+ */
+public interface Encoder {
+ /**
+ * Context of the current serialization process, including contextual and polymorphic serialization and,
+ * potentially, a format-specific configuration.
+ */
+ public val serializersModule: SerializersModule
+
+ /**
+ * Notifies the encoder that value of a nullable type that is
+ * being serialized is not null. It should be called before writing a non-null value
+ * of nullable type:
+ * ```
+ * // Could be String? serialize method
+ * if (value != null) {
+ * encoder.encodeNotNullMark()
+ * encoder.encodeStringValue(value)
+ * } else {
+ * encoder.encodeNull()
+ * }
+ * ```
+ *
+ * This method has a use in highly-performant binary formats and can
+ * be safely ignore by most of the regular formats.
+ */
+ @ExperimentalSerializationApi
+ public fun encodeNotNullMark() {}
+
+ /**
+ * Encodes `null` value.
+ */
+ @ExperimentalSerializationApi
+ public fun encodeNull()
+
+ /**
+ * Encodes a boolean value.
+ * Corresponding kind is [PrimitiveKind.BOOLEAN].
+ */
+ public fun encodeBoolean(value: Boolean)
+
+ /**
+ * Encodes a single byte value.
+ * Corresponding kind is [PrimitiveKind.BYTE].
+ */
+ public fun encodeByte(value: Byte)
+
+ /**
+ * Encodes a 16-bit short value.
+ * Corresponding kind is [PrimitiveKind.SHORT].
+ */
+ public fun encodeShort(value: Short)
+
+ /**
+ * Encodes a 16-bit unicode character value.
+ * Corresponding kind is [PrimitiveKind.CHAR].
+ */
+ public fun encodeChar(value: Char)
+
+ /**
+ * Encodes a 32-bit integer value.
+ * Corresponding kind is [PrimitiveKind.INT].
+ */
+ public fun encodeInt(value: Int)
+
+ /**
+ * Encodes a 64-bit integer value.
+ * Corresponding kind is [PrimitiveKind.LONG].
+ */
+ public fun encodeLong(value: Long)
+
+ /**
+ * Encodes a 32-bit IEEE 754 floating point value.
+ * Corresponding kind is [PrimitiveKind.FLOAT].
+ */
+ public fun encodeFloat(value: Float)
+
+ /**
+ * Encodes a 64-bit IEEE 754 floating point value.
+ * Corresponding kind is [PrimitiveKind.DOUBLE].
+ */
+ public fun encodeDouble(value: Double)
+
+ /**
+ * Encodes a string value.
+ * Corresponding kind is [PrimitiveKind.STRING].
+ */
+ public fun encodeString(value: String)
+
+ /**
+ * Encodes a enum value that is stored at the [index] in [enumDescriptor] elements collection.
+ * Corresponding kind is [SerialKind.ENUM].
+ *
+ * E.g. for the enum `enum class Letters { A, B, C, D }` and
+ * serializable value "C", [encodeEnum] method should be called with `2` as am index.
+ *
+ * This method does not imply any restrictions on the output format,
+ * the format is free to store the enum by its name, index, ordinal or any other
+ */
+ public fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int)
+
+ /**
+ * Returns [Encoder] for encoding an underlying type of an inline class.
+ * [inlineDescriptor] describes a serializable inline class.
+ *
+ * Namely, for the `@Serializable inline class MyInt(val my: Int)`,
+ * the following sequence is used:
+ * ```
+ * thisEncoder.encodeInline(MyInt.serializer().descriptor).encodeInt(my)
+ * ```
+ *
+ * Current encoder may return any other instance of [Encoder] class,
+ * depending on the provided [inlineDescriptor].
+ * For example, when this function is called on Json encoder with
+ * `UInt.serializer().descriptor`, the returned encoder is able
+ * to encode unsigned integers.
+ *
+ * Note that this function returns [Encoder] instead of [CompositeEncoder]
+ * because inline classes always have one property.
+ * Calling [Encoder.beginStructure] on returned instance leads to an undefined behavior.
+ */
+ @ExperimentalSerializationApi
+ public fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder
+
+ /**
+ * Encodes the beginning of the nested structure in a serialized form
+ * and returns [CompositeDecoder] responsible for encoding this very structure.
+ * E.g the hierarchy:
+ * ```
+ * class StringHolder(val stringValue: String)
+ * class Holder(val stringHolder: StringHolder)
+ * ```
+ *
+ * with the following serialized form in JSON:
+ * ```
+ * {
+ * "stringHolder" : { "stringValue": "value" }
+ * }
+ * ```
+ *
+ * will be roughly represented as the following sequence of calls:
+ * ```
+ * // Holder serializer
+ * fun serialize(encoder: Encoder, value: Holder) {
+ * val composite = encoder.beginStructure(descriptor) // the very first opening bracket '{'
+ * composite.encodeSerializableElement(descriptor, 0, value.stringHolder) // Serialize nested StringHolder
+ * composite.endStructure(descriptor) // The very last closing bracket
+ * }
+ *
+ * // StringHolder serializer
+ * fun serialize(encoder: Encoder, value: StringHolder) {
+ * val composite = encoder.beginStructure(descriptor) // One more '{' when the key "stringHolder" is already written
+ * composite.encodeStringElement(descriptor, 0, value.stringValue) // Serialize actual value
+ * composite.endStructure(descriptor) // Closing bracket
+ * }
+ * ```
+ */
+ public fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder
+
+ /**
+ * Encodes the beginning of the collection with size [collectionSize] and the given serializer of its type parameters.
+ * This method has to be implemented only if you need to know collection size in advance, otherwise, [beginStructure] can be used.
+ */
+ public fun beginCollection(
+ descriptor: SerialDescriptor,
+ collectionSize: Int
+ ): CompositeEncoder = beginStructure(descriptor)
+
+ /**
+ * Encodes the [value] of type [T] by delegating the encoding process to the given [serializer].
+ * For example, `encodeInt` call us equivalent to delegating integer encoding to [Int.serializer][Int.Companion.serializer]:
+ * `encodeSerializableValue(Int.serializer())`
+ */
+ public fun <T : Any?> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
+ serializer.serialize(this, value)
+ }
+
+ /**
+ * Encodes the nullable [value] of type [T] by delegating the encoding process to the given [serializer].
+ */
+ @Suppress("UNCHECKED_CAST")
+ @ExperimentalSerializationApi
+ public fun <T : Any> encodeNullableSerializableValue(serializer: SerializationStrategy<T>, value: T?) {
+ val isNullabilitySupported = serializer.descriptor.isNullable
+ if (isNullabilitySupported) {
+ // Instead of `serializer.serialize` to be able to intercept this
+ return encodeSerializableValue(serializer as SerializationStrategy<T?>, value)
+ }
+
+ // Else default path used to avoid allocation of NullableSerializer
+ if (value == null) {
+ encodeNull()
+ } else {
+ encodeNotNullMark()
+ encodeSerializableValue(serializer, value)
+ }
+ }
+}
+
+/**
+ * [CompositeEncoder] is a part of encoding process that is bound to a particular structured part of
+ * the serialized form, described by the serial descriptor passed to [Encoder.beginStructure].
+ *
+ * All `encode*` methods have `index` and `serialDescriptor` parameters with a strict semantics and constraints:
+ * * `descriptor` is always the same as one used in [Encoder.beginStructure]. While this parameter may seem redundant,
+ * it is required for efficient serialization process to avoid excessive field spilling.
+ * If you are writing your own format, you can safely ignore this parameter and use one used in `beginStructure`
+ * for simplicity.
+ * * `index` of the element being encoded. This element at this index in the descriptor should be associated with
+ * the one being written.
+ *
+ * The symmetric interface for the deserialization process is [CompositeDecoder].
+ *
+ * ### Not stable for inheritance
+ *
+ * `CompositeEncoder` interface is not stable for inheritance in 3rd party libraries, as new methods
+ * might be added to this interface or contracts of the existing methods can be changed.
+ */
+public interface CompositeEncoder {
+ /**
+ * Context of the current serialization process, including contextual and polymorphic serialization and,
+ * potentially, a format-specific configuration.
+ */
+ public val serializersModule: SerializersModule
+
+ /**
+ * Denotes the end of the structure associated with current encoder.
+ * For example, composite encoder of JSON format will write
+ * a closing bracket in the underlying input and reduce the number of nesting for pretty printing.
+ */
+ public fun endStructure(descriptor: SerialDescriptor)
+
+ /**
+ * Whether the format should encode values that are equal to the default values.
+ * This method is used by plugin-generated serializers for properties with default values:
+ * ```
+ * @Serializable
+ * class WithDefault(val int: Int = 42)
+ * // serialize method
+ * if (value.int != 42 || output.shouldEncodeElementDefault(serialDesc, 0)) {
+ * encoder.encodeIntElement(serialDesc, 0, value.int);
+ * }
+ * ```
+ *
+ * This method is never invoked for properties annotated with [EncodeDefault].
+ */
+ @ExperimentalSerializationApi
+ public fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean = true
+
+ /**
+ * Encodes a boolean [value] associated with an element at the given [index] in [serial descriptor][descriptor].
+ * The element at the given [index] should have [PrimitiveKind.BOOLEAN] kind.
+ */
+ public fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean)
+
+ /**
+ * Encodes a single byte [value] associated with an element at the given [index] in [serial descriptor][descriptor].
+ * The element at the given [index] should have [PrimitiveKind.BYTE] kind.
+ */
+ public fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte)
+
+ /**
+ * Encodes a 16-bit short [value] associated with an element at the given [index] in [serial descriptor][descriptor].
+ * The element at the given [index] should have [PrimitiveKind.SHORT] kind.
+ */
+ public fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short)
+
+ /**
+ * Encodes a 16-bit unicode character [value] associated with an element at the given [index] in [serial descriptor][descriptor].
+ * The element at the given [index] should have [PrimitiveKind.CHAR] kind.
+ */
+ public fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char)
+
+ /**
+ * Encodes a 32-bit integer [value] associated with an element at the given [index] in [serial descriptor][descriptor].
+ * The element at the given [index] should have [PrimitiveKind.INT] kind.
+ */
+ public fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int)
+
+ /**
+ * Encodes a 64-bit integer [value] associated with an element at the given [index] in [serial descriptor][descriptor].
+ * The element at the given [index] should have [PrimitiveKind.LONG] kind.
+ */
+ public fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long)
+
+ /**
+ * Encodes a 32-bit IEEE 754 floating point [value] associated with an element
+ * at the given [index] in [serial descriptor][descriptor].
+ * The element at the given [index] should have [PrimitiveKind.FLOAT] kind.
+ */
+ public fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float)
+
+ /**
+ * Encodes a 64-bit IEEE 754 floating point [value] associated with an element
+ * at the given [index] in [serial descriptor][descriptor].
+ * The element at the given [index] should have [PrimitiveKind.DOUBLE] kind.
+ */
+ public fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double)
+
+ /**
+ * Encodes a string [value] associated with an element at the given [index] in [serial descriptor][descriptor].
+ * The element at the given [index] should have [PrimitiveKind.STRING] kind.
+ */
+ public fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String)
+
+ /**
+ * Returns [Encoder] for decoding an underlying type of an inline class.
+ * Serializable inline class is described by the [child descriptor][SerialDescriptor.getElementDescriptor]
+ * of given [descriptor] at [index].
+ *
+ * Namely, for the `@Serializable inline class MyInt(val my: Int)`,
+ * and `@Serializable class MyData(val myInt: MyInt)`
+ * the following sequence is used:
+ * ```
+ * thisEncoder.encodeInlineElement(MyData.serializer.descriptor, 0).encodeInt(my)
+ * ```
+ *
+ * This method is an optimization and its invocation should have the exact same result as
+ * ```
+ * thisEncoder.encodeSerializableElement(MyData.serializer.descriptor, 0, MyInt.serializer(), myInt)
+ * ```
+ *
+ * Current encoder may return any other instance of [Encoder] class,
+ * depending on provided descriptor.
+ * For example, when this function is called on Json encoder with descriptor that has
+ * `UInt.serializer().descriptor` at the given [index], the returned encoder is able
+ * to encode unsigned integers.
+ *
+ * Note that this function returns [Encoder] instead of [CompositeEncoder]
+ * because inline classes always have one property.
+ * Calling [Encoder.beginStructure] on returned instance leads to an undefined behavior.
+ *
+ * @see Encoder.encodeInline
+ * @see SerialDescriptor.getElementDescriptor
+ */
+ @ExperimentalSerializationApi
+ public fun encodeInlineElement(
+ descriptor: SerialDescriptor,
+ index: Int
+ ): Encoder
+
+ /**
+ * Delegates [value] encoding of the type [T] to the given [serializer].
+ * [value] is associated with an element at the given [index] in [serial descriptor][descriptor].
+ */
+ public fun <T : Any?> encodeSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ serializer: SerializationStrategy<T>,
+ value: T
+ )
+
+ /**
+ * Delegates nullable [value] encoding of the type [T] to the given [serializer].
+ * [value] is associated with an element at the given [index] in [serial descriptor][descriptor].
+ */
+ @ExperimentalSerializationApi
+ public fun <T : Any> encodeNullableSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ serializer: SerializationStrategy<T>,
+ value: T?
+ )
+}
+
+/**
+ * Begins a structure, encodes it using the given [block] and ends it.
+ */
+public inline fun Encoder.encodeStructure(
+ descriptor: SerialDescriptor,
+ crossinline block: CompositeEncoder.() -> Unit
+) {
+ val composite = beginStructure(descriptor)
+ composite.block()
+ composite.endStructure(descriptor)
+}
+
+/**
+ * Begins a collection, encodes it using the given [block] and ends it.
+ */
+public inline fun Encoder.encodeCollection(
+ descriptor: SerialDescriptor,
+ collectionSize: Int,
+ crossinline block: CompositeEncoder.() -> Unit
+) {
+ val composite = beginCollection(descriptor, collectionSize)
+ composite.block()
+ composite.endStructure(descriptor)
+}
+
+/**
+ * Begins a collection, calls [block] with each item and ends the collections.
+ */
+public inline fun <E> Encoder.encodeCollection(
+ descriptor: SerialDescriptor,
+ collection: Collection<E>,
+ crossinline block: CompositeEncoder.(index: Int, E) -> Unit
+) {
+ encodeCollection(descriptor, collection.size) {
+ collection.forEachIndexed { index, e ->
+ block(index, e)
+ }
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/AbstractPolymorphicSerializer.kt b/core/commonMain/src/kotlinx/serialization/internal/AbstractPolymorphicSerializer.kt
new file mode 100644
index 00000000..a85b4659
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/AbstractPolymorphicSerializer.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.encoding.*
+import kotlin.jvm.*
+import kotlin.reflect.*
+
+/**
+ * Base class for providing multiplatform polymorphic serialization.
+ *
+ * This class cannot be implemented by library users. To learn how to use it for your case,
+ * please refer to [PolymorphicSerializer] for interfaces/abstract classes and [SealedClassSerializer] for sealed classes.
+ *
+ * By default, without special support from [Encoder], polymorphic types are serialized as list with
+ * two elements: class [serial name][SerialDescriptor.serialName] (String) and the object itself.
+ * Serial name equals to fully-qualified class name by default and can be changed via @[SerialName] annotation.
+ */
+@InternalSerializationApi
+@OptIn(ExperimentalSerializationApi::class)
+public abstract class AbstractPolymorphicSerializer<T : Any> internal constructor() : KSerializer<T> {
+
+ /**
+ * Base class for all classes that this polymorphic serializer can serialize or deserialize.
+ */
+ public abstract val baseClass: KClass<T>
+
+ public final override fun serialize(encoder: Encoder, value: T) {
+ val actualSerializer = findPolymorphicSerializer(encoder, value)
+ encoder.encodeStructure(descriptor) {
+ encodeStringElement(descriptor, 0, actualSerializer.descriptor.serialName)
+ encodeSerializableElement(descriptor, 1, actualSerializer.cast(), value)
+ }
+ }
+
+ public final override fun deserialize(decoder: Decoder): T = decoder.decodeStructure(descriptor) {
+ var klassName: String? = null
+ var value: Any? = null
+ if (decodeSequentially()) {
+ return@decodeStructure decodeSequentially(this)
+ }
+
+ mainLoop@ while (true) {
+ when (val index = decodeElementIndex(descriptor)) {
+ CompositeDecoder.DECODE_DONE -> {
+ break@mainLoop
+ }
+ 0 -> {
+ klassName = decodeStringElement(descriptor, index)
+ }
+ 1 -> {
+ klassName = requireNotNull(klassName) { "Cannot read polymorphic value before its type token" }
+ val serializer = findPolymorphicSerializer(this, klassName)
+ value = decodeSerializableElement(descriptor, index, serializer)
+ }
+ else -> throw SerializationException(
+ "Invalid index in polymorphic deserialization of " +
+ (klassName ?: "unknown class") +
+ "\n Expected 0, 1 or DECODE_DONE(-1), but found $index"
+ )
+ }
+ }
+ @Suppress("UNCHECKED_CAST")
+ requireNotNull(value) { "Polymorphic value has not been read for class $klassName" } as T
+ }
+
+ private fun decodeSequentially(compositeDecoder: CompositeDecoder): T {
+ val klassName = compositeDecoder.decodeStringElement(descriptor, 0)
+ val serializer = findPolymorphicSerializer(compositeDecoder, klassName)
+ return compositeDecoder.decodeSerializableElement(descriptor, 1, serializer)
+ }
+
+ /**
+ * Lookups an actual serializer for given [klassName] withing the current [base class][baseClass].
+ * May use context from the [decoder].
+ */
+ @InternalSerializationApi
+ public open fun findPolymorphicSerializerOrNull(
+ decoder: CompositeDecoder,
+ klassName: String?
+ ): DeserializationStrategy<out T>? = decoder.serializersModule.getPolymorphic(baseClass, klassName)
+
+
+ /**
+ * Lookups an actual serializer for given [value] within the current [base class][baseClass].
+ * May use context from the [encoder].
+ */
+ @InternalSerializationApi
+ public open fun findPolymorphicSerializerOrNull(
+ encoder: Encoder,
+ value: T
+ ): SerializationStrategy<T>? =
+ encoder.serializersModule.getPolymorphic(baseClass, value)
+}
+
+@JvmName("throwSubtypeNotRegistered")
+internal fun throwSubtypeNotRegistered(subClassName: String?, baseClass: KClass<*>): Nothing {
+ val scope = "in the scope of '${baseClass.simpleName}'"
+ throw SerializationException(
+ if (subClassName == null)
+ "Class discriminator was missing and no default polymorphic serializers were registered $scope"
+ else
+ "Class '$subClassName' is not registered for polymorphic serialization $scope.\n" +
+ "Mark the base class as 'sealed' or register the serializer explicitly."
+ )
+}
+
+@JvmName("throwSubtypeNotRegistered")
+internal fun throwSubtypeNotRegistered(subClass: KClass<*>, baseClass: KClass<*>): Nothing =
+ throwSubtypeNotRegistered(subClass.simpleName ?: "$subClass", baseClass)
diff --git a/core/commonMain/src/kotlinx/serialization/internal/CachedNames.kt b/core/commonMain/src/kotlinx/serialization/internal/CachedNames.kt
new file mode 100644
index 00000000..a5b7dfde
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/CachedNames.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.descriptors.*
+
+/**
+ * Internal interface used as a marker for [SerialDescriptor] in order
+ * to retrieve the set of all element names without allocations.
+ * Used by our implementations as a performance optimization.
+ * It's not an instance of [SerialDescriptor] to simplify implementation via delegation
+ */
+internal interface CachedNames {
+
+ /**
+ * A set of all names retrieved from [SerialDescriptor.getElementName]
+ */
+ public val serialNames: Set<String>
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/CollectionDescriptors.kt b/core/commonMain/src/kotlinx/serialization/internal/CollectionDescriptors.kt
new file mode 100644
index 00000000..dc7c4e81
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/CollectionDescriptors.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+
+@ExperimentalSerializationApi
+internal sealed class ListLikeDescriptor(val elementDescriptor: SerialDescriptor) : SerialDescriptor {
+ override val kind: SerialKind get() = StructureKind.LIST
+ override val elementsCount: Int = 1
+
+ override fun getElementName(index: Int): String = index.toString()
+ override fun getElementIndex(name: String): Int =
+ name.toIntOrNull() ?: throw IllegalArgumentException("$name is not a valid list index")
+
+ override fun isElementOptional(index: Int): Boolean {
+ require(index >= 0) { "Illegal index $index, $serialName expects only non-negative indices"}
+ return false
+ }
+
+ override fun getElementAnnotations(index: Int): List<Annotation> {
+ require(index >= 0) { "Illegal index $index, $serialName expects only non-negative indices"}
+ return emptyList()
+ }
+
+ override fun getElementDescriptor(index: Int): SerialDescriptor {
+ require(index >= 0) { "Illegal index $index, $serialName expects only non-negative indices"}
+ return elementDescriptor
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is ListLikeDescriptor) return false
+ if (elementDescriptor == other.elementDescriptor && serialName == other.serialName) return true
+ return false
+ }
+
+ override fun hashCode(): Int {
+ return elementDescriptor.hashCode() * 31 + serialName.hashCode()
+ }
+
+ override fun toString(): String = "$serialName($elementDescriptor)"
+}
+
+internal sealed class MapLikeDescriptor(
+ override val serialName: String,
+ val keyDescriptor: SerialDescriptor,
+ val valueDescriptor: SerialDescriptor
+) : SerialDescriptor {
+ override val kind: SerialKind get() = StructureKind.MAP
+ override val elementsCount: Int = 2
+ override fun getElementName(index: Int): String = index.toString()
+ override fun getElementIndex(name: String): Int =
+ name.toIntOrNull() ?: throw IllegalArgumentException("$name is not a valid map index")
+
+ override fun isElementOptional(index: Int): Boolean {
+ require(index >= 0) { "Illegal index $index, $serialName expects only non-negative indices"}
+ return false
+ }
+
+ override fun getElementAnnotations(index: Int): List<Annotation> {
+ require(index >= 0) { "Illegal index $index, $serialName expects only non-negative indices"}
+ return emptyList()
+ }
+
+ override fun getElementDescriptor(index: Int): SerialDescriptor {
+ require(index >= 0) { "Illegal index $index, $serialName expects only non-negative indices"}
+ return when (index % 2) {
+ 0 -> keyDescriptor
+ 1 -> valueDescriptor
+ else -> error("Unreached")
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is MapLikeDescriptor) return false
+ if (serialName != other.serialName) return false
+ if (keyDescriptor != other.keyDescriptor) return false
+ if (valueDescriptor != other.valueDescriptor) return false
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = serialName.hashCode()
+ result = 31 * result + keyDescriptor.hashCode()
+ result = 31 * result + valueDescriptor.hashCode()
+ return result
+ }
+
+ override fun toString(): String = "$serialName($keyDescriptor, $valueDescriptor)"
+}
+
+internal const val ARRAY_NAME = "kotlin.Array"
+internal const val ARRAY_LIST_NAME = "kotlin.collections.ArrayList"
+internal const val LINKED_HASH_SET_NAME = "kotlin.collections.LinkedHashSet"
+internal const val HASH_SET_NAME = "kotlin.collections.HashSet"
+internal const val LINKED_HASH_MAP_NAME = "kotlin.collections.LinkedHashMap"
+internal const val HASH_MAP_NAME = "kotlin.collections.HashMap"
+
+/**
+ * Descriptor for primitive arrays, such as [IntArray], [DoubleArray], etc...
+ *
+ * Can be obtained from corresponding serializers (e.g. [ByteArraySerializer.descriptor])
+ */
+@OptIn(ExperimentalSerializationApi::class)
+internal class PrimitiveArrayDescriptor internal constructor(
+ primitive: SerialDescriptor
+) : ListLikeDescriptor(primitive) {
+ override val serialName: String = "${primitive.serialName}Array"
+}
+
+internal class ArrayClassDesc(elementDesc: SerialDescriptor) : ListLikeDescriptor(elementDesc) {
+ override val serialName: String get() = ARRAY_NAME
+}
+
+internal class ArrayListClassDesc(elementDesc: SerialDescriptor) : ListLikeDescriptor(elementDesc) {
+ override val serialName: String get() = ARRAY_LIST_NAME
+}
+
+internal class LinkedHashSetClassDesc(elementDesc: SerialDescriptor) : ListLikeDescriptor(elementDesc) {
+ override val serialName: String get() = LINKED_HASH_SET_NAME
+}
+
+internal class HashSetClassDesc(elementDesc: SerialDescriptor) : ListLikeDescriptor(elementDesc) {
+ override val serialName: String get() = HASH_SET_NAME
+}
+
+internal class LinkedHashMapClassDesc(keyDesc: SerialDescriptor, valueDesc: SerialDescriptor) :
+ MapLikeDescriptor(LINKED_HASH_MAP_NAME, keyDesc, valueDesc)
+
+internal class HashMapClassDesc(keyDesc: SerialDescriptor, valueDesc: SerialDescriptor) :
+ MapLikeDescriptor(HASH_MAP_NAME, keyDesc, valueDesc)
diff --git a/core/commonMain/src/kotlinx/serialization/internal/CollectionSerializers.kt b/core/commonMain/src/kotlinx/serialization/internal/CollectionSerializers.kt
new file mode 100644
index 00000000..fcf4cfa7
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/CollectionSerializers.kt
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:Suppress("DEPRECATION_ERROR")
+@file:OptIn(ExperimentalSerializationApi::class)
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlin.reflect.*
+
+@InternalSerializationApi
+public sealed class AbstractCollectionSerializer<Element, Collection, Builder> : KSerializer<Collection> {
+ protected abstract fun Collection.collectionSize(): Int
+ protected abstract fun Collection.collectionIterator(): Iterator<Element>
+ protected abstract fun builder(): Builder
+ protected abstract fun Builder.builderSize(): Int
+ protected abstract fun Builder.toResult(): Collection
+ protected abstract fun Collection.toBuilder(): Builder
+ protected abstract fun Builder.checkCapacity(size: Int)
+
+ abstract override fun serialize(encoder: Encoder, value: Collection)
+
+ @InternalSerializationApi
+ public fun merge(decoder: Decoder, previous: Collection?): Collection {
+ val builder = previous?.toBuilder() ?: builder()
+ val startIndex = builder.builderSize()
+ val compositeDecoder = decoder.beginStructure(descriptor)
+ if (compositeDecoder.decodeSequentially()) {
+ readAll(compositeDecoder, builder, startIndex, readSize(compositeDecoder, builder))
+ } else {
+ while (true) {
+ val index = compositeDecoder.decodeElementIndex(descriptor)
+ if (index == CompositeDecoder.DECODE_DONE) break
+ readElement(compositeDecoder, startIndex + index, builder)
+ }
+ }
+ compositeDecoder.endStructure(descriptor)
+ return builder.toResult()
+ }
+
+ override fun deserialize(decoder: Decoder): Collection = merge(decoder, null)
+
+ private fun readSize(decoder: CompositeDecoder, builder: Builder): Int {
+ val size = decoder.decodeCollectionSize(descriptor)
+ builder.checkCapacity(size)
+ return size
+ }
+
+ protected abstract fun readElement(decoder: CompositeDecoder, index: Int, builder: Builder, checkIndex: Boolean = true)
+
+ protected abstract fun readAll(decoder: CompositeDecoder, builder: Builder, startIndex: Int, size: Int)
+}
+
+@PublishedApi
+internal sealed class CollectionLikeSerializer<Element, Collection, Builder>(
+ private val elementSerializer: KSerializer<Element>
+) : AbstractCollectionSerializer<Element, Collection, Builder>() {
+
+ protected abstract fun Builder.insert(index: Int, element: Element)
+ abstract override val descriptor: SerialDescriptor
+
+ override fun serialize(encoder: Encoder, value: Collection) {
+ val size = value.collectionSize()
+ encoder.encodeCollection(descriptor, size) {
+ val iterator = value.collectionIterator()
+ for (index in 0 until size)
+ encodeSerializableElement(descriptor, index, elementSerializer, iterator.next())
+ }
+ }
+
+ final override fun readAll(decoder: CompositeDecoder, builder: Builder, startIndex: Int, size: Int) {
+ require(size >= 0) { "Size must be known in advance when using READ_ALL" }
+ for (index in 0 until size)
+ readElement(decoder, startIndex + index, builder, checkIndex = false)
+ }
+
+ override fun readElement(decoder: CompositeDecoder, index: Int, builder: Builder, checkIndex: Boolean) {
+ builder.insert(index, decoder.decodeSerializableElement(descriptor, index, elementSerializer))
+ }
+}
+
+@InternalSerializationApi // TODO tech debt: it's used in ProtoBuf
+public sealed class MapLikeSerializer<Key, Value, Collection, Builder : MutableMap<Key, Value>>(
+ public val keySerializer: KSerializer<Key>,
+ public val valueSerializer: KSerializer<Value>
+) : AbstractCollectionSerializer<Map.Entry<Key, Value>, Collection, Builder>() {
+
+ protected abstract fun Builder.insertKeyValuePair(index: Int, key: Key, value: Value)
+ abstract override val descriptor: SerialDescriptor
+
+ protected final override fun readAll(decoder: CompositeDecoder, builder: Builder, startIndex: Int, size: Int) {
+ require(size >= 0) { "Size must be known in advance when using READ_ALL" }
+ for (index in 0 until size * 2 step 2)
+ readElement(decoder, startIndex + index, builder, checkIndex = false)
+ }
+
+ final override fun readElement(decoder: CompositeDecoder, index: Int, builder: Builder, checkIndex: Boolean) {
+ val key: Key = decoder.decodeSerializableElement(descriptor, index, keySerializer)
+ val vIndex = if (checkIndex) {
+ decoder.decodeElementIndex(descriptor).also {
+ require(it == index + 1) { "Value must follow key in a map, index for key: $index, returned index for value: $it" }
+ }
+ } else {
+ index + 1
+ }
+ val value: Value = if (builder.containsKey(key) && valueSerializer.descriptor.kind !is PrimitiveKind) {
+ decoder.decodeSerializableElement(descriptor, vIndex, valueSerializer, builder.getValue(key))
+ } else {
+ decoder.decodeSerializableElement(descriptor, vIndex, valueSerializer)
+ }
+ builder[key] = value
+ }
+
+ override fun serialize(encoder: Encoder, value: Collection) {
+ val size = value.collectionSize()
+ encoder.encodeCollection(descriptor, size) {
+ val iterator = value.collectionIterator()
+ var index = 0
+ iterator.forEach { (k, v) ->
+ encodeSerializableElement(descriptor, index++, keySerializer, k)
+ encodeSerializableElement(descriptor, index++, valueSerializer, v)
+ }
+ }
+ }
+}
+
+@PublishedApi
+internal abstract class PrimitiveArrayBuilder<Array> internal constructor() {
+ internal abstract val position: Int
+ internal abstract fun ensureCapacity(requiredCapacity: Int = position + 1)
+ internal abstract fun build(): Array
+}
+
+/**
+ * Base serializer for all serializers for primitive arrays.
+ *
+ * It exists only to avoid code duplication and should not be used or implemented directly.
+ * Use concrete serializers ([ByteArraySerializer], etc) instead.
+ */
+@PublishedApi
+internal abstract class PrimitiveArraySerializer<Element, Array, Builder
+: PrimitiveArrayBuilder<Array>> internal constructor(
+ primitiveSerializer: KSerializer<Element>
+) : CollectionLikeSerializer<Element, Array, Builder>(primitiveSerializer) {
+ final override val descriptor: SerialDescriptor = PrimitiveArrayDescriptor(primitiveSerializer.descriptor)
+
+ final override fun Builder.builderSize(): Int = position
+ final override fun Builder.toResult(): Array = build()
+ final override fun Builder.checkCapacity(size: Int): Unit = ensureCapacity(size)
+
+ final override fun Array.collectionIterator(): Iterator<Element> =
+ error("This method lead to boxing and must not be used, use writeContents instead")
+
+ final override fun Builder.insert(index: Int, element: Element): Unit =
+ error("This method lead to boxing and must not be used, use Builder.append instead")
+
+ final override fun builder(): Builder = empty().toBuilder()
+
+ protected abstract fun empty(): Array
+
+ abstract override fun readElement(
+ decoder: CompositeDecoder,
+ index: Int,
+ builder: Builder,
+ checkIndex: Boolean
+ )
+
+ protected abstract fun writeContent(encoder: CompositeEncoder, content: Array, size: Int)
+
+ final override fun serialize(encoder: Encoder, value: Array) {
+ val size = value.collectionSize()
+ encoder.encodeCollection(descriptor, size) {
+ writeContent(this, value, size)
+ }
+ }
+
+ final override fun deserialize(decoder: Decoder): Array = merge(decoder, null)
+}
+
+// todo: can be more efficient when array size is know in advance, this one always uses temporary ArrayList as builder
+@PublishedApi
+internal class ReferenceArraySerializer<ElementKlass : Any, Element : ElementKlass?>(
+ private val kClass: KClass<ElementKlass>,
+ eSerializer: KSerializer<Element>
+) : CollectionLikeSerializer<Element, Array<Element>, ArrayList<Element>>(eSerializer) {
+ override val descriptor: SerialDescriptor = ArrayClassDesc(eSerializer.descriptor)
+
+ override fun Array<Element>.collectionSize(): Int = size
+ override fun Array<Element>.collectionIterator(): Iterator<Element> = iterator()
+ override fun builder(): ArrayList<Element> = arrayListOf()
+ override fun ArrayList<Element>.builderSize(): Int = size
+
+ @Suppress("UNCHECKED_CAST")
+ override fun ArrayList<Element>.toResult(): Array<Element> = toNativeArrayImpl<ElementKlass, Element>(kClass)
+
+ override fun Array<Element>.toBuilder(): ArrayList<Element> = ArrayList(this.asList())
+ override fun ArrayList<Element>.checkCapacity(size: Int): Unit = ensureCapacity(size)
+ override fun ArrayList<Element>.insert(index: Int, element: Element) {
+ add(index, element)
+ }
+}
+
+@PublishedApi
+internal abstract class CollectionSerializer<E, C: Collection<E>, B>(element: KSerializer<E>) : CollectionLikeSerializer<E, C, B>(element) {
+ override fun C.collectionSize(): Int = size
+ override fun C.collectionIterator(): Iterator<E> = iterator()
+}
+
+@InternalSerializationApi
+@PublishedApi
+internal class ArrayListSerializer<E>(element: KSerializer<E>) : CollectionSerializer<E, List<E>, ArrayList<E>>(element) {
+ override val descriptor: SerialDescriptor = ArrayListClassDesc(element.descriptor)
+
+ override fun builder(): ArrayList<E> = arrayListOf()
+ override fun ArrayList<E>.builderSize(): Int = size
+ override fun ArrayList<E>.toResult(): List<E> = this
+ override fun List<E>.toBuilder(): ArrayList<E> = this as? ArrayList<E> ?: ArrayList(this)
+ override fun ArrayList<E>.checkCapacity(size: Int): Unit = ensureCapacity(size)
+ override fun ArrayList<E>.insert(index: Int, element: E) { add(index, element) }
+}
+
+@PublishedApi
+internal class LinkedHashSetSerializer<E>(
+ eSerializer: KSerializer<E>
+) : CollectionSerializer<E, Set<E>, LinkedHashSet<E>>(eSerializer) {
+ override val descriptor: SerialDescriptor = LinkedHashSetClassDesc(eSerializer.descriptor)
+
+ override fun builder(): LinkedHashSet<E> = linkedSetOf()
+ override fun LinkedHashSet<E>.builderSize(): Int = size
+ override fun LinkedHashSet<E>.toResult(): Set<E> = this
+ override fun Set<E>.toBuilder(): LinkedHashSet<E> = this as? LinkedHashSet<E> ?: LinkedHashSet(this)
+ override fun LinkedHashSet<E>.checkCapacity(size: Int) {}
+ override fun LinkedHashSet<E>.insert(index: Int, element: E) { add(element) }
+}
+
+@PublishedApi
+internal class HashSetSerializer<E>(
+ eSerializer: KSerializer<E>
+) : CollectionSerializer<E, Set<E>, HashSet<E>>(eSerializer) {
+ override val descriptor: SerialDescriptor = HashSetClassDesc(eSerializer.descriptor)
+
+ override fun builder(): HashSet<E> = HashSet()
+ override fun HashSet<E>.builderSize(): Int = size
+ override fun HashSet<E>.toResult(): Set<E> = this
+ override fun Set<E>.toBuilder(): HashSet<E> = this as? HashSet<E> ?: HashSet(this)
+ override fun HashSet<E>.checkCapacity(size: Int) {}
+ override fun HashSet<E>.insert(index: Int, element: E) { add(element) }
+}
+
+@PublishedApi
+internal class LinkedHashMapSerializer<K, V>(
+ kSerializer: KSerializer<K>, vSerializer: KSerializer<V>
+) : MapLikeSerializer<K, V, Map<K, V>, LinkedHashMap<K, V>>(kSerializer, vSerializer) {
+
+ override val descriptor: SerialDescriptor = LinkedHashMapClassDesc(kSerializer.descriptor, vSerializer.descriptor)
+ override fun Map<K, V>.collectionSize(): Int = size
+ override fun Map<K, V>.collectionIterator(): Iterator<Map.Entry<K, V>> = iterator()
+ override fun builder(): LinkedHashMap<K, V> = LinkedHashMap()
+ override fun LinkedHashMap<K, V>.builderSize(): Int = size * 2
+ override fun LinkedHashMap<K, V>.toResult(): Map<K, V> = this
+ override fun Map<K, V>.toBuilder(): LinkedHashMap<K, V> = this as? LinkedHashMap<K, V> ?: LinkedHashMap(this)
+ override fun LinkedHashMap<K, V>.checkCapacity(size: Int) {}
+ override fun LinkedHashMap<K, V>.insertKeyValuePair(index: Int, key: K, value: V): Unit = set(key, value)
+}
+
+@PublishedApi
+internal class HashMapSerializer<K, V>(
+ kSerializer: KSerializer<K>, vSerializer: KSerializer<V>
+) : MapLikeSerializer<K, V, Map<K, V>, HashMap<K, V>>(kSerializer, vSerializer) {
+
+ override val descriptor: SerialDescriptor = HashMapClassDesc(kSerializer.descriptor, vSerializer.descriptor)
+ override fun Map<K, V>.collectionSize(): Int = size
+ override fun Map<K, V>.collectionIterator(): Iterator<Map.Entry<K, V>> = iterator()
+ override fun builder(): HashMap<K, V> = HashMap()
+ override fun HashMap<K, V>.builderSize(): Int = size * 2
+ override fun HashMap<K, V>.toResult(): Map<K, V> = this
+ override fun Map<K, V>.toBuilder(): HashMap<K, V> = this as? HashMap<K, V> ?: HashMap(this)
+ override fun HashMap<K, V>.checkCapacity(size: Int) {}
+ override fun HashMap<K, V>.insertKeyValuePair(index: Int, key: K, value: V): Unit = set(key, value)
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/ElementMarker.kt b/core/commonMain/src/kotlinx/serialization/internal/ElementMarker.kt
new file mode 100644
index 00000000..fca90262
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/ElementMarker.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.CompositeDecoder
+
+@OptIn(ExperimentalSerializationApi::class)
+@PublishedApi
+internal class ElementMarker(
+ private val descriptor: SerialDescriptor,
+ // Instead of inheritance and virtual function in order to keep cross-module internal modifier via suppresses
+ // Can be reworked via public + internal api if necessary
+ private val readIfAbsent: (SerialDescriptor, Int) -> Boolean
+) {
+ /*
+ * Element decoding marks from given bytes.
+ * The element index is the same as the set bit position.
+ * Marks for the lowest 64 elements are always stored in a single Long value, higher elements stores in long array.
+ */
+ private var lowerMarks: Long
+ private val highMarksArray: LongArray
+
+ private companion object {
+ private val EMPTY_HIGH_MARKS = LongArray(0)
+ }
+
+ init {
+ val elementsCount = descriptor.elementsCount
+ if (elementsCount <= Long.SIZE_BITS) {
+ lowerMarks = if (elementsCount == Long.SIZE_BITS) {
+ // number of bits in the mark is equal to the number of fields
+ 0L
+ } else {
+ // (1 - elementsCount) bits are always 1 since there are no fields for them
+ -1L shl elementsCount
+ }
+ highMarksArray = EMPTY_HIGH_MARKS
+ } else {
+ lowerMarks = 0L
+ highMarksArray = prepareHighMarksArray(elementsCount)
+ }
+ }
+
+ fun mark(index: Int) {
+ if (index < Long.SIZE_BITS) {
+ lowerMarks = lowerMarks or (1L shl index)
+ } else {
+ markHigh(index)
+ }
+ }
+
+ fun nextUnmarkedIndex(): Int {
+ val elementsCount = descriptor.elementsCount
+ while (lowerMarks != -1L) {
+ val index = lowerMarks.inv().countTrailingZeroBits()
+ lowerMarks = lowerMarks or (1L shl index)
+
+ if (readIfAbsent(descriptor, index)) {
+ return index
+ }
+ }
+
+ if (elementsCount > Long.SIZE_BITS) {
+ return nextUnmarkedHighIndex()
+ }
+ return CompositeDecoder.DECODE_DONE
+ }
+
+ private fun prepareHighMarksArray(elementsCount: Int): LongArray {
+ // (elementsCount - 1) / Long.SIZE_BITS
+ // (elementsCount - 1) because only one Long value is needed to store 64 fields etc
+ val slotsCount = (elementsCount - 1) ushr 6
+ // elementsCount % Long.SIZE_BITS
+ val elementsInLastSlot = elementsCount and (Long.SIZE_BITS - 1)
+ val highMarks = LongArray(slotsCount)
+ // if (elementsCount % Long.SIZE_BITS) == 0 means that the fields occupy all bits in mark
+ if (elementsInLastSlot != 0) {
+ // all marks except the higher are always 0
+ highMarks[highMarks.lastIndex] = -1L shl elementsCount
+ }
+ return highMarks
+ }
+
+ private fun markHigh(index: Int) {
+ // (index / Long.SIZE_BITS) - 1
+ val slot = (index ushr 6) - 1
+ // index % Long.SIZE_BITS
+ val offsetInSlot = index and (Long.SIZE_BITS - 1)
+ highMarksArray[slot] = highMarksArray[slot] or (1L shl offsetInSlot)
+ }
+
+ private fun nextUnmarkedHighIndex(): Int {
+ for (slot in highMarksArray.indices) {
+ // (slot + 1) because first element in high marks has index 64
+ val slotOffset = (slot + 1) * Long.SIZE_BITS
+ // store in a variable so as not to frequently use the array
+ var slotMarks = highMarksArray[slot]
+
+ while (slotMarks != -1L) {
+ val indexInSlot = slotMarks.inv().countTrailingZeroBits()
+ slotMarks = slotMarks or (1L shl indexInSlot)
+
+ val index = slotOffset + indexInSlot
+ if (readIfAbsent(descriptor, index)) {
+ highMarksArray[slot] = slotMarks
+ return index
+ }
+ }
+ highMarksArray[slot] = slotMarks
+ }
+ return CompositeDecoder.DECODE_DONE
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/Enums.kt b/core/commonMain/src/kotlinx/serialization/internal/Enums.kt
new file mode 100644
index 00000000..1e620154
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/Enums.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+/*
+ * Descriptor used for explicitly serializable enums by the plugin.
+ * Designed to be consistent with `EnumSerializer.descriptor` and weird plugin usage.
+ */
+@Suppress("unused") // Used by the plugin
+@PublishedApi
+@OptIn(ExperimentalSerializationApi::class)
+internal class EnumDescriptor(
+ name: String,
+ elementsCount: Int
+) : PluginGeneratedSerialDescriptor(name, elementsCount = elementsCount) {
+
+ override val kind: SerialKind = SerialKind.ENUM
+ private val elementDescriptors by lazy {
+ Array(elementsCount) { buildSerialDescriptor(name + "." + getElementName(it), StructureKind.OBJECT) }
+ }
+
+ override fun getElementDescriptor(index: Int): SerialDescriptor = elementDescriptors.getChecked(index)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null) return false
+ if (other !is SerialDescriptor) return false
+ if (other.kind !== SerialKind.ENUM) return false
+ if (serialName != other.serialName) return false
+ if (cachedSerialNames() != other.cachedSerialNames()) return false
+ return true
+ }
+
+ override fun toString(): String {
+ return elementNames.joinToString(", ", "$serialName(", ")")
+ }
+
+ override fun hashCode(): Int {
+ var result = serialName.hashCode()
+ val elementsHashCode = elementNames.elementsHashCodeBy { it }
+ result = 31 * result + elementsHashCode
+ return result
+ }
+}
+
+// Used for enums that are not explicitly serializable by the plugin
+@PublishedApi
+@OptIn(ExperimentalSerializationApi::class)
+internal class EnumSerializer<T : Enum<T>>(
+ serialName: String,
+ private val values: Array<T>
+) : KSerializer<T> {
+
+ override val descriptor: SerialDescriptor = buildSerialDescriptor(serialName, SerialKind.ENUM) {
+ values.forEach {
+ val fqn = "$serialName.${it.name}"
+ val enumMemberDescriptor = buildSerialDescriptor(fqn, StructureKind.OBJECT)
+ element(it.name, enumMemberDescriptor)
+ }
+ }
+
+ override fun serialize(encoder: Encoder, value: T) {
+ val index = values.indexOf(value)
+ if (index == -1) {
+ throw SerializationException(
+ "$value is not a valid enum ${descriptor.serialName}, " +
+ "must be one of ${values.contentToString()}"
+ )
+ }
+ encoder.encodeEnum(descriptor, index)
+ }
+
+ override fun deserialize(decoder: Decoder): T {
+ val index = decoder.decodeEnum(descriptor)
+ if (index !in values.indices) {
+ throw SerializationException(
+ "$index is not among valid ${descriptor.serialName} enum values, " +
+ "values size is ${values.size}"
+ )
+ }
+ return values[index]
+ }
+
+ override fun toString(): String = "kotlinx.serialization.internal.EnumSerializer<${descriptor.serialName}>"
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/InlineClassDescriptor.kt b/core/commonMain/src/kotlinx/serialization/internal/InlineClassDescriptor.kt
new file mode 100644
index 00000000..05fd92be
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/InlineClassDescriptor.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+@Suppress("Unused")
+@PublishedApi
+@OptIn(ExperimentalSerializationApi::class)
+internal class InlineClassDescriptor(
+ name: String,
+ generatedSerializer: GeneratedSerializer<*>
+) : PluginGeneratedSerialDescriptor(name, generatedSerializer, 1) {
+
+ override val isInline: Boolean = true
+
+ override fun hashCode(): Int = super.hashCode() * 31
+
+ override fun equals(other: Any?): Boolean = equalsImpl(other) { otherDescriptor ->
+ otherDescriptor.isInline &&
+ typeParameterDescriptors.contentEquals(otherDescriptor.typeParameterDescriptors)
+ }
+}
+
+internal fun <T> InlinePrimitiveDescriptor(name: String, primitiveSerializer: KSerializer<T>): SerialDescriptor =
+ InlineClassDescriptor(name, object : GeneratedSerializer<T> {
+ // object needed only to pass childSerializers()
+ override fun childSerializers(): Array<KSerializer<*>> = arrayOf(primitiveSerializer)
+
+ override val descriptor: SerialDescriptor get() = error("unsupported")
+
+ override fun serialize(encoder: Encoder, value: T) {
+ error("unsupported")
+ }
+
+ override fun deserialize(decoder: Decoder): T {
+ error("unsupported")
+ }
+ })
diff --git a/core/commonMain/src/kotlinx/serialization/internal/InlineClasses.kt b/core/commonMain/src/kotlinx/serialization/internal/InlineClasses.kt
new file mode 100644
index 00000000..b9738908
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/InlineClasses.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+@PublishedApi
+@ExperimentalSerializationApi
+@ExperimentalUnsignedTypes
+internal object UIntSerializer : KSerializer<UInt> {
+ override val descriptor: SerialDescriptor = InlinePrimitiveDescriptor("kotlin.UInt", Int.serializer())
+
+ override fun serialize(encoder: Encoder, value: UInt) {
+ encoder.encodeInline(descriptor).encodeInt(value.toInt())
+ }
+
+ override fun deserialize(decoder: Decoder): UInt {
+ return decoder.decodeInline(descriptor).decodeInt().toUInt()
+ }
+}
+
+@PublishedApi
+@ExperimentalSerializationApi
+@ExperimentalUnsignedTypes
+internal object ULongSerializer : KSerializer<ULong> {
+ override val descriptor: SerialDescriptor = InlinePrimitiveDescriptor("kotlin.ULong", Long.serializer())
+
+ override fun serialize(encoder: Encoder, value: ULong) {
+ encoder.encodeInline(descriptor).encodeLong(value.toLong())
+ }
+
+ override fun deserialize(decoder: Decoder): ULong {
+ return decoder.decodeInline(descriptor).decodeLong().toULong()
+ }
+}
+
+@PublishedApi
+@ExperimentalSerializationApi
+@ExperimentalUnsignedTypes
+internal object UByteSerializer : KSerializer<UByte> {
+ override val descriptor: SerialDescriptor = InlinePrimitiveDescriptor("kotlin.UByte", Byte.serializer())
+
+ override fun serialize(encoder: Encoder, value: UByte) {
+ encoder.encodeInline(descriptor).encodeByte(value.toByte())
+ }
+
+ override fun deserialize(decoder: Decoder): UByte {
+ return decoder.decodeInline(descriptor).decodeByte().toUByte()
+ }
+}
+
+@PublishedApi
+@ExperimentalSerializationApi
+@ExperimentalUnsignedTypes
+internal object UShortSerializer : KSerializer<UShort> {
+ override val descriptor: SerialDescriptor = InlinePrimitiveDescriptor("kotlin.UShort", Short.serializer())
+
+ override fun serialize(encoder: Encoder, value: UShort) {
+ encoder.encodeInline(descriptor).encodeShort(value.toShort())
+ }
+
+ override fun deserialize(decoder: Decoder): UShort {
+ return decoder.decodeInline(descriptor).decodeShort().toUShort()
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/JsonInternalDependencies.kt b/core/commonMain/src/kotlinx/serialization/internal/JsonInternalDependencies.kt
new file mode 100644
index 00000000..d79cb8b9
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/JsonInternalDependencies.kt
@@ -0,0 +1,14 @@
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+
+/*
+ * Methods that are required for kotlinx-serialization-json, but are not effectively public
+ * and actually represent our own technical debt.
+ * This methods are not intended for public use
+ */
+
+@InternalSerializationApi
+@Deprecated(message = "Should not be used", level = DeprecationLevel.ERROR)
+public fun SerialDescriptor.jsonCachedSerialNames(): Set<String> = cachedSerialNames()
diff --git a/core/commonMain/src/kotlinx/serialization/internal/NoOpEncoder.kt b/core/commonMain/src/kotlinx/serialization/internal/NoOpEncoder.kt
new file mode 100644
index 00000000..463e4863
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/NoOpEncoder.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+/**
+ * Encoder that does not do any operations. Its main purpose is to ignore data instead of writing it.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+internal object NoOpEncoder : AbstractEncoder() {
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ public override fun encodeValue(value: Any): Unit = Unit
+
+ override fun encodeNull(): Unit = Unit
+
+ override fun encodeBoolean(value: Boolean): Unit = Unit
+ override fun encodeByte(value: Byte): Unit = Unit
+ override fun encodeShort(value: Short): Unit = Unit
+ override fun encodeInt(value: Int): Unit = Unit
+ override fun encodeLong(value: Long): Unit = Unit
+ override fun encodeFloat(value: Float): Unit = Unit
+ override fun encodeDouble(value: Double): Unit = Unit
+ override fun encodeChar(value: Char): Unit = Unit
+ override fun encodeString(value: String): Unit = Unit
+ override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int): Unit = Unit
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/NullableSerializer.kt b/core/commonMain/src/kotlinx/serialization/internal/NullableSerializer.kt
new file mode 100644
index 00000000..73a4c1af
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/NullableSerializer.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+/**
+ * Use [KSerializer.nullable][nullable] instead.
+ * @suppress internal API
+ */
+@PublishedApi
+@OptIn(ExperimentalSerializationApi::class)
+internal class NullableSerializer<T : Any>(private val serializer: KSerializer<T>) : KSerializer<T?> {
+ override val descriptor: SerialDescriptor = SerialDescriptorForNullable(serializer.descriptor)
+
+ override fun serialize(encoder: Encoder, value: T?) {
+ if (value != null) {
+ encoder.encodeNotNullMark()
+ encoder.encodeSerializableValue(serializer, value)
+ } else {
+ encoder.encodeNull()
+ }
+ }
+
+ override fun deserialize(decoder: Decoder): T? {
+ return if (decoder.decodeNotNullMark()) decoder.decodeSerializableValue(serializer) else decoder.decodeNull()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+ other as NullableSerializer<*>
+ if (serializer != other.serializer) return false
+ return true
+ }
+
+ override fun hashCode(): Int {
+ return serializer.hashCode()
+ }
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal class SerialDescriptorForNullable(
+ internal val original: SerialDescriptor
+) : SerialDescriptor by original, CachedNames {
+
+ override val serialName: String = original.serialName + "?"
+ override val serialNames: Set<String> = original.cachedSerialNames()
+ override val isNullable: Boolean
+ get() = true
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is SerialDescriptorForNullable) return false
+ if (original != other.original) return false
+ return true
+ }
+
+ override fun toString(): String {
+ return "$original?"
+ }
+
+ override fun hashCode(): Int {
+ return original.hashCode() * 31
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/ObjectSerializer.kt b/core/commonMain/src/kotlinx/serialization/internal/ObjectSerializer.kt
new file mode 100644
index 00000000..ebb9e2e4
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/ObjectSerializer.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+/**
+ * Serializer for Kotlin's singletons (denoted by `object` keyword).
+ * To preserve singleton identity after serialization and deserialization, object serializer
+ * uses an [object instance][objectInstance].
+ * By default, a singleton is serialized as an empty structure, e.g. `{}` in JSON.
+ */
+@PublishedApi
+@OptIn(ExperimentalSerializationApi::class)
+internal class ObjectSerializer<T : Any>(serialName: String, private val objectInstance: T) : KSerializer<T> {
+
+ @PublishedApi // See comment in SealedClassSerializer
+ internal constructor(
+ serialName: String,
+ objectInstance: T,
+ classAnnotations: Array<Annotation>
+ ) : this(serialName, objectInstance) {
+ _annotations = classAnnotations.asList()
+ }
+
+ private var _annotations: List<Annotation> = emptyList()
+
+ override val descriptor: SerialDescriptor by lazy(LazyThreadSafetyMode.PUBLICATION) {
+ buildSerialDescriptor(serialName, StructureKind.OBJECT) {
+ annotations = _annotations
+ }
+ }
+
+ override fun serialize(encoder: Encoder, value: T) {
+ encoder.beginStructure(descriptor).endStructure(descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): T {
+ decoder.decodeStructure(descriptor) {
+ when (val index = decodeElementIndex(descriptor)) {
+ CompositeDecoder.DECODE_DONE -> {
+ return@decodeStructure
+ }
+ else -> throw SerializationException("Unexpected index $index")
+ }
+ }
+ return objectInstance
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/Platform.common.kt b/core/commonMain/src/kotlinx/serialization/internal/Platform.common.kt
new file mode 100644
index 00000000..fde2c36a
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/Platform.common.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlin.native.concurrent.*
+import kotlin.reflect.*
+
+internal object InternalHexConverter {
+ private const val hexCode = "0123456789ABCDEF"
+
+ fun parseHexBinary(s: String): ByteArray {
+ val len = s.length
+ require(len % 2 == 0) { "HexBinary string must be even length" }
+ val bytes = ByteArray(len / 2)
+ var i = 0
+
+ while (i < len) {
+ val h = hexToInt(s[i])
+ val l = hexToInt(s[i + 1])
+ require(!(h == -1 || l == -1)) { "Invalid hex chars: ${s[i]}${s[i+1]}" }
+
+ bytes[i / 2] = ((h shl 4) + l).toByte()
+ i += 2
+ }
+
+ return bytes
+ }
+
+ private fun hexToInt(ch: Char): Int = when (ch) {
+ in '0'..'9' -> ch - '0'
+ in 'A'..'F' -> ch - 'A' + 10
+ in 'a'..'f' -> ch - 'a' + 10
+ else -> -1
+ }
+
+ fun printHexBinary(data: ByteArray, lowerCase: Boolean = false): String {
+ val r = StringBuilder(data.size * 2)
+ for (b in data) {
+ r.append(hexCode[b.toInt() shr 4 and 0xF])
+ r.append(hexCode[b.toInt() and 0xF])
+ }
+ return if (lowerCase) r.toString().lowercase() else r.toString()
+ }
+
+ fun toHexString(n: Int): String {
+ val arr = ByteArray(4)
+ for (i in 0 until 4) {
+ arr[i] = (n shr (24 - i * 8)).toByte()
+ }
+ return printHexBinary(arr, true).trimStart('0').takeIf { it.isNotEmpty() } ?: "0"
+ }
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.cachedSerialNames(): Set<String> {
+ if (this is CachedNames) return serialNames
+ val result = HashSet<String>(elementsCount)
+ for (i in 0 until elementsCount) {
+ result += getElementName(i)
+ }
+ return result
+}
+
+@SharedImmutable
+private val EMPTY_DESCRIPTOR_ARRAY: Array<SerialDescriptor> = arrayOf()
+
+/**
+ * Same as [toTypedArray], but uses special empty array constant, if [this]
+ * is null or empty.
+ */
+internal fun List<SerialDescriptor>?.compactArray(): Array<SerialDescriptor> =
+ takeUnless { it.isNullOrEmpty() }?.toTypedArray() ?: EMPTY_DESCRIPTOR_ARRAY
+
+@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
+@PublishedApi
+internal inline fun <T> KSerializer<*>.cast(): KSerializer<T> = this as KSerializer<T>
+
+@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
+@PublishedApi
+internal inline fun <T> SerializationStrategy<*>.cast(): SerializationStrategy<T> = this as SerializationStrategy<T>
+
+@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
+@PublishedApi
+internal inline fun <T> DeserializationStrategy<*>.cast(): DeserializationStrategy<T> = this as DeserializationStrategy<T>
+
+internal fun KClass<*>.serializerNotRegistered(): Nothing {
+ throw SerializationException(
+ "Serializer for class '${simpleName}' is not found.\n" +
+ "Mark the class as @Serializable or provide the serializer explicitly."
+ )
+}
+
+internal expect fun KClass<*>.platformSpecificSerializerNotRegistered(): Nothing
+
+@Suppress("UNCHECKED_CAST")
+internal fun KType.kclass() = when (val t = classifier) {
+ is KClass<*> -> t
+ is KTypeParameter -> {
+ error("Captured type paramerer $t from generic non-reified function. " +
+ "Such functionality cannot be supported as $t is erased, either specify serializer explicitly or make " +
+ "calling function inline with reified $t")
+ }
+ else -> error("Only KClass supported as classifier, got $t")
+} as KClass<Any>
+
+/**
+ * Constructs KSerializer<D<T0, T1, ...>> by given KSerializer<T0>, KSerializer<T1>, ...
+ * via reflection (on JVM) or compiler+plugin intrinsic `SerializerFactory` (on Native)
+ */
+internal expect fun <T : Any> KClass<T>.constructSerializerForGivenTypeArgs(vararg args: KSerializer<Any?>): KSerializer<T>?
+
+/**
+ * Checks whether given KType and its corresponding KClass represent a reference array
+ */
+internal expect fun isReferenceArray(rootClass: KClass<Any>): Boolean
+
+/**
+ * Array.get that checks indices on JS
+ */
+internal expect fun <T> Array<T>.getChecked(index: Int): T
+
+/**
+ * Array.get that checks indices on JS
+ */
+internal expect fun BooleanArray.getChecked(index: Int): Boolean
+
+internal expect fun <T : Any> KClass<T>.compiledSerializerImpl(): KSerializer<T>?
+
+internal expect fun <T : Any, E : T?> ArrayList<E>.toNativeArrayImpl(eClass: KClass<T>): Array<E>
+
+/**
+ * Checks whether the receiver is an instance of a given kclass.
+ *
+ * This check is a replacement for [KClass.isInstance] because on JVM it requires kotlin-reflect.jar in classpath (see KT-14720).
+ *
+ * On JS and Native, this function delegates to aforementioned [KClass.isInstance] since it is supported there out-of-the-box;
+ * on JVM, it falls back to `java.lang.Class.isInstance`, which causes difference when applied to function types with big arity.
+ */
+internal expect fun Any.isInstanceOf(kclass: KClass<*>): Boolean
+
+internal inline fun <T, K> Iterable<T>.elementsHashCodeBy(selector: (T) -> K): Int {
+ return fold(1) { hash, element -> 31 * hash + selector(element).hashCode() }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/PluginExceptions.kt b/core/commonMain/src/kotlinx/serialization/internal/PluginExceptions.kt
new file mode 100644
index 00000000..f07296eb
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/PluginExceptions.kt
@@ -0,0 +1,40 @@
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.InternalSerializationApi
+import kotlinx.serialization.MissingFieldException
+import kotlinx.serialization.descriptors.SerialDescriptor
+
+@OptIn(ExperimentalSerializationApi::class)
+@InternalSerializationApi
+public fun throwMissingFieldException(seen: Int, goldenMask: Int, descriptor: SerialDescriptor) {
+ val missingFields = mutableListOf<String>()
+
+ var missingFieldsBits = goldenMask and seen.inv()
+ for (i in 0 until 32) {
+ if (missingFieldsBits and 1 != 0) {
+ missingFields += descriptor.getElementName(i)
+ }
+ missingFieldsBits = missingFieldsBits ushr 1
+ }
+ throw MissingFieldException(missingFields, descriptor.serialName)
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+@InternalSerializationApi
+public fun throwArrayMissingFieldException(seenArray: IntArray, goldenMaskArray: IntArray, descriptor: SerialDescriptor) {
+ val missingFields = mutableListOf<String>()
+
+ for (maskSlot in goldenMaskArray.indices) {
+ var missingFieldsBits = goldenMaskArray[maskSlot] and seenArray[maskSlot].inv()
+ if (missingFieldsBits != 0) {
+ for (i in 0 until 32) {
+ if (missingFieldsBits and 1 != 0) {
+ missingFields += descriptor.getElementName(maskSlot * 32 + i)
+ }
+ missingFieldsBits = missingFieldsBits ushr 1
+ }
+ }
+ }
+ throw MissingFieldException(missingFields, descriptor.serialName)
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor.kt b/core/commonMain/src/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor.kt
new file mode 100644
index 00000000..7b6efe7e
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:Suppress("OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE", "UNUSED")
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME
+
+/**
+ * Implementation that plugin uses to implement descriptors for auto-generated serializers.
+ */
+@PublishedApi
+@OptIn(ExperimentalSerializationApi::class)
+internal open class PluginGeneratedSerialDescriptor(
+ override val serialName: String,
+ private val generatedSerializer: GeneratedSerializer<*>? = null,
+ final override val elementsCount: Int
+) : SerialDescriptor, CachedNames {
+ override val kind: SerialKind get() = StructureKind.CLASS
+ override val annotations: List<Annotation> get() = classAnnotations ?: emptyList()
+
+ private var added = -1
+ private val names = Array(elementsCount) { "[UNINITIALIZED]" }
+ private val propertiesAnnotations = arrayOfNulls<MutableList<Annotation>?>(elementsCount)
+
+ // Classes rarely have annotations, so we can save up a bit of allocations here
+ private var classAnnotations: MutableList<Annotation>? = null
+ private val elementsOptionality = BooleanArray(elementsCount)
+ public override val serialNames: Set<String> get() = indices.keys
+
+ private var indices: Map<String, Int> = emptyMap()
+ // Cache child serializers, they are not cached by the implementation for nullable types
+ private val childSerializers: Array<KSerializer<*>> by lazy(LazyThreadSafetyMode.PUBLICATION) { generatedSerializer?.childSerializers() ?: EMPTY_SERIALIZER_ARRAY }
+
+ // Lazy because of JS specific initialization order (#789)
+ internal val typeParameterDescriptors: Array<SerialDescriptor> by lazy(LazyThreadSafetyMode.PUBLICATION) {
+ generatedSerializer?.typeParametersSerializers()?.map { it.descriptor }.compactArray()
+ }
+
+ // Can be without synchronization but Native will likely break due to freezing
+ private val _hashCode: Int by lazy(LazyThreadSafetyMode.PUBLICATION) { hashCodeImpl(typeParameterDescriptors) }
+
+ public fun addElement(name: String, isOptional: Boolean = false) {
+ names[++added] = name
+ elementsOptionality[added] = isOptional
+ propertiesAnnotations[added] = null
+ if (added == elementsCount - 1) {
+ indices = buildIndices()
+ }
+ }
+
+ public fun pushAnnotation(annotation: Annotation) {
+ val list = propertiesAnnotations[added].let {
+ if (it == null) {
+ val result = ArrayList<Annotation>(1)
+ propertiesAnnotations[added] = result
+ result
+ } else {
+ it
+ }
+ }
+ list.add(annotation)
+ }
+
+ public fun pushClassAnnotation(a: Annotation) {
+ if (classAnnotations == null) {
+ classAnnotations = ArrayList(1)
+ }
+ classAnnotations!!.add(a)
+ }
+
+ override fun getElementDescriptor(index: Int): SerialDescriptor {
+ return childSerializers.getChecked(index).descriptor
+ }
+
+ override fun isElementOptional(index: Int): Boolean = elementsOptionality.getChecked(index)
+ override fun getElementAnnotations(index: Int): List<Annotation> =
+ propertiesAnnotations.getChecked(index) ?: emptyList()
+ override fun getElementName(index: Int): String = names.getChecked(index)
+ override fun getElementIndex(name: String): Int = indices[name] ?: UNKNOWN_NAME
+
+ private fun buildIndices(): Map<String, Int> {
+ val indices = HashMap<String, Int>()
+ for (i in names.indices) {
+ indices[names[i]] = i
+ }
+ return indices
+ }
+
+ override fun equals(other: Any?): Boolean = equalsImpl(other) { otherDescriptor ->
+ typeParameterDescriptors.contentEquals(otherDescriptor.typeParameterDescriptors)
+ }
+
+ override fun hashCode(): Int = _hashCode
+
+ override fun toString(): String {
+ return (0 until elementsCount).joinToString(", ", "$serialName(", ")") { i ->
+ getElementName(i) + ": " + getElementDescriptor(i).serialName
+ }
+ }
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal inline fun <reified SD : SerialDescriptor> SD.equalsImpl(
+ other: Any?,
+ typeParamsAreEqual: (otherDescriptor: SD) -> Boolean
+): Boolean {
+ if (this === other) return true
+ if (other !is SD) return false
+ if (serialName != other.serialName) return false
+ if (!typeParamsAreEqual(other)) return false
+ if (this.elementsCount != other.elementsCount) return false
+ for (index in 0 until elementsCount) {
+ if (getElementDescriptor(index).serialName != other.getElementDescriptor(index).serialName) return false
+ if (getElementDescriptor(index).kind != other.getElementDescriptor(index).kind) return false
+ }
+ return true
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.hashCodeImpl(typeParams: Array<SerialDescriptor>): Int {
+ var result = serialName.hashCode()
+ result = 31 * result + typeParams.contentHashCode()
+ val elementDescriptors = elementDescriptors
+ val namesHash = elementDescriptors.elementsHashCodeBy { it.serialName }
+ val kindHash = elementDescriptors.elementsHashCodeBy { it.kind }
+ result = 31 * result + namesHash
+ result = 31 * result + kindHash
+ return result
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/PluginHelperInterfaces.kt b/core/commonMain/src/kotlinx/serialization/internal/PluginHelperInterfaces.kt
new file mode 100644
index 00000000..f613c2ad
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/PluginHelperInterfaces.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlin.jvm.*
+import kotlin.native.concurrent.*
+
+@SharedImmutable
+@JvmField
+internal val EMPTY_SERIALIZER_ARRAY: Array<KSerializer<*>> = arrayOf()
+
+/**
+ * An interface for a [KSerializer] instance generated by the compiler plugin.
+ *
+ * Should not be implemented manually or used directly.
+ */
+@InternalSerializationApi
+public interface GeneratedSerializer<T> : KSerializer<T> {
+ public fun childSerializers(): Array<KSerializer<*>>
+ public fun typeParametersSerializers(): Array<KSerializer<*>> = EMPTY_SERIALIZER_ARRAY
+}
+
+/**
+ * An internal interface used by the compiler plugin for objects that are factories of typed serializers, for example
+ * for auto-generated companion objects for [Serializable] classes with type parameters.
+ *
+ * This interface is used to lookup and create serializers in K/N using `@AssociatedObjectKey`.
+ * Should not be used in any user code. Please use generated `.serializer(kSerializer1, kSerializer2, ...)`
+ * method on a companion or top-level `serializer(KType)` function.
+ */
+@Deprecated("Inserted into generated code and should not be used directly", level = DeprecationLevel.HIDDEN)
+public interface SerializerFactory {
+ public fun serializer(vararg typeParamsSerializers: KSerializer<*>): KSerializer<*>
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/PrimitiveArraysSerializers.kt b/core/commonMain/src/kotlinx/serialization/internal/PrimitiveArraysSerializers.kt
new file mode 100644
index 00000000..7427b160
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/PrimitiveArraysSerializers.kt
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:Suppress("DEPRECATION_ERROR")
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.encoding.*
+
+private const val INITIAL_SIZE = 10
+
+/**
+ * Serializer for [ByteArray].
+ *
+ * Encode elements one-by-one, as regular list,
+ * unless format's Encoder/Decoder have special handling for this serializer.
+ */
+@PublishedApi
+internal object ByteArraySerializer : KSerializer<ByteArray>,
+ PrimitiveArraySerializer<Byte, ByteArray, ByteArrayBuilder>(Byte.serializer()) {
+
+ override fun ByteArray.collectionSize(): Int = size
+ override fun ByteArray.toBuilder(): ByteArrayBuilder = ByteArrayBuilder(this)
+ override fun empty(): ByteArray = ByteArray(0)
+
+ override fun readElement(decoder: CompositeDecoder, index: Int, builder: ByteArrayBuilder, checkIndex: Boolean) {
+ builder.append(decoder.decodeByteElement(descriptor, index))
+ }
+
+ override fun writeContent(encoder: CompositeEncoder, content: ByteArray, size: Int) {
+ for (i in 0 until size)
+ encoder.encodeByteElement(descriptor, i, content[i])
+ }
+}
+
+@PublishedApi
+internal class ByteArrayBuilder internal constructor(
+ bufferWithData: ByteArray
+) : PrimitiveArrayBuilder<ByteArray>() {
+
+ private var buffer: ByteArray = bufferWithData
+ override var position: Int = bufferWithData.size
+ private set
+
+ init {
+ ensureCapacity(INITIAL_SIZE)
+ }
+
+ override fun ensureCapacity(requiredCapacity: Int) {
+ if (buffer.size < requiredCapacity)
+ buffer = buffer.copyOf(requiredCapacity.coerceAtLeast(buffer.size * 2))
+ }
+
+ internal fun append(c: Byte) {
+ ensureCapacity()
+ buffer[position++] = c
+ }
+
+ override fun build() = buffer.copyOf(position)
+}
+
+// the rest of the serializers are merely copy-paste
+/**
+ * Serializer for [ShortArray].
+ *
+ * Encode elements one-by-one, as regular list,
+ * unless format's Encoder/Decoder have special handling for this serializer.
+ */
+@PublishedApi
+internal object ShortArraySerializer : KSerializer<ShortArray>,
+ PrimitiveArraySerializer<Short, ShortArray, ShortArrayBuilder>(Short.serializer()) {
+
+ override fun ShortArray.collectionSize(): Int = size
+ override fun ShortArray.toBuilder(): ShortArrayBuilder = ShortArrayBuilder(this)
+ override fun empty(): ShortArray = ShortArray(0)
+
+ override fun readElement(decoder: CompositeDecoder, index: Int, builder: ShortArrayBuilder, checkIndex: Boolean) {
+ builder.append(decoder.decodeShortElement(descriptor, index))
+ }
+
+ override fun writeContent(encoder: CompositeEncoder, content: ShortArray, size: Int) {
+ for (i in 0 until size)
+ encoder.encodeShortElement(descriptor, i, content[i])
+ }
+}
+
+@PublishedApi
+internal class ShortArrayBuilder internal constructor(
+ bufferWithData: ShortArray
+) : PrimitiveArrayBuilder<ShortArray>() {
+
+ private var buffer: ShortArray = bufferWithData
+ override var position: Int = bufferWithData.size
+ private set
+
+ init {
+ ensureCapacity(INITIAL_SIZE)
+ }
+
+ override fun ensureCapacity(requiredCapacity: Int) {
+ if (buffer.size < requiredCapacity)
+ buffer = buffer.copyOf(requiredCapacity.coerceAtLeast(buffer.size * 2))
+ }
+
+ internal fun append(c: Short) {
+ ensureCapacity()
+ buffer[position++] = c
+ }
+
+ override fun build() = buffer.copyOf(position)
+}
+
+/**
+ * Serializer for [IntArray].
+ *
+ * Encode elements one-by-one, as regular list,
+ * unless format's Encoder/Decoder have special handling for this serializer.
+ */
+@PublishedApi
+internal object IntArraySerializer : KSerializer<IntArray>,
+ PrimitiveArraySerializer<Int, IntArray, IntArrayBuilder>(Int.serializer()) {
+
+ override fun IntArray.collectionSize(): Int = size
+ override fun IntArray.toBuilder(): IntArrayBuilder = IntArrayBuilder(this)
+ override fun empty(): IntArray = IntArray(0)
+
+ override fun readElement(decoder: CompositeDecoder, index: Int, builder: IntArrayBuilder, checkIndex: Boolean) {
+ builder.append(decoder.decodeIntElement(descriptor, index))
+ }
+
+ override fun writeContent(encoder: CompositeEncoder, content: IntArray, size: Int) {
+ for (i in 0 until size)
+ encoder.encodeIntElement(descriptor, i, content[i])
+ }
+}
+
+@PublishedApi
+internal class IntArrayBuilder internal constructor(
+ bufferWithData: IntArray
+) : PrimitiveArrayBuilder<IntArray>() {
+
+ private var buffer: IntArray = bufferWithData
+ override var position: Int = bufferWithData.size
+ private set
+
+ init {
+ ensureCapacity(INITIAL_SIZE)
+ }
+
+ override fun ensureCapacity(requiredCapacity: Int) {
+ if (buffer.size < requiredCapacity)
+ buffer = buffer.copyOf(requiredCapacity.coerceAtLeast(buffer.size * 2))
+ }
+
+ internal fun append(c: Int) {
+ ensureCapacity()
+ buffer[position++] = c
+ }
+
+ override fun build() = buffer.copyOf(position)
+}
+
+/**
+ * Serializer for [LongArray].
+ *
+ * Encode elements one-by-one, as regular list,
+ * unless format's Encoder/Decoder have special handling for this serializer.
+ */
+@PublishedApi
+internal object LongArraySerializer : KSerializer<LongArray>,
+ PrimitiveArraySerializer<Long, LongArray, LongArrayBuilder>(Long.serializer()) {
+
+ override fun LongArray.collectionSize(): Int = size
+ override fun LongArray.toBuilder(): LongArrayBuilder = LongArrayBuilder(this)
+ override fun empty(): LongArray = LongArray(0)
+
+ override fun readElement(decoder: CompositeDecoder, index: Int, builder: LongArrayBuilder, checkIndex: Boolean) {
+ builder.append(decoder.decodeLongElement(descriptor, index))
+ }
+
+ override fun writeContent(encoder: CompositeEncoder, content: LongArray, size: Int) {
+ for (i in 0 until size)
+ encoder.encodeLongElement(descriptor, i, content[i])
+ }
+}
+
+@PublishedApi
+internal class LongArrayBuilder internal constructor(
+ bufferWithData: LongArray
+) : PrimitiveArrayBuilder<LongArray>() {
+
+ private var buffer: LongArray = bufferWithData
+ override var position: Int = bufferWithData.size
+ private set
+
+ init {
+ ensureCapacity(INITIAL_SIZE)
+ }
+
+ override fun ensureCapacity(requiredCapacity: Int) {
+ if (buffer.size < requiredCapacity)
+ buffer = buffer.copyOf(requiredCapacity.coerceAtLeast(buffer.size * 2))
+ }
+
+ internal fun append(c: Long) {
+ ensureCapacity()
+ buffer[position++] = c
+ }
+
+ override fun build() = buffer.copyOf(position)
+}
+
+/**
+ * Serializer for [FloatArray].
+ *
+ * Encode elements one-by-one, as regular list,
+ * unless format's Encoder/Decoder have special handling for this serializer.
+ */
+@PublishedApi
+internal object FloatArraySerializer : KSerializer<FloatArray>,
+ PrimitiveArraySerializer<Float, FloatArray, FloatArrayBuilder>(Float.serializer()) {
+
+ override fun FloatArray.collectionSize(): Int = size
+ override fun FloatArray.toBuilder(): FloatArrayBuilder = FloatArrayBuilder(this)
+ override fun empty(): FloatArray = FloatArray(0)
+
+ override fun readElement(decoder: CompositeDecoder, index: Int, builder: FloatArrayBuilder, checkIndex: Boolean) {
+ builder.append(decoder.decodeFloatElement(descriptor, index))
+ }
+
+ override fun writeContent(encoder: CompositeEncoder, content: FloatArray, size: Int) {
+ for (i in 0 until size)
+ encoder.encodeFloatElement(descriptor, i, content[i])
+ }
+}
+
+@PublishedApi
+internal class FloatArrayBuilder internal constructor(
+ bufferWithData: FloatArray
+) : PrimitiveArrayBuilder<FloatArray>() {
+
+ private var buffer: FloatArray = bufferWithData
+ override var position: Int = bufferWithData.size
+ private set
+
+ init {
+ ensureCapacity(INITIAL_SIZE)
+ }
+
+ override fun ensureCapacity(requiredCapacity: Int) {
+ if (buffer.size < requiredCapacity)
+ buffer = buffer.copyOf(requiredCapacity.coerceAtLeast(buffer.size * 2))
+ }
+
+ internal fun append(c: Float) {
+ ensureCapacity()
+ buffer[position++] = c
+ }
+
+ override fun build() = buffer.copyOf(position)
+}
+
+/**
+ * Serializer for [DoubleArray].
+ *
+ * Encode elements one-by-one, as regular list,
+ * unless format's Encoder/Decoder have special handling for this serializer.
+ */
+@PublishedApi
+internal object DoubleArraySerializer : KSerializer<DoubleArray>,
+ PrimitiveArraySerializer<Double, DoubleArray, DoubleArrayBuilder>(Double.serializer()) {
+
+ override fun DoubleArray.collectionSize(): Int = size
+ override fun DoubleArray.toBuilder(): DoubleArrayBuilder = DoubleArrayBuilder(this)
+ override fun empty(): DoubleArray = DoubleArray(0)
+
+ override fun readElement(decoder: CompositeDecoder, index: Int, builder: DoubleArrayBuilder, checkIndex: Boolean) {
+ builder.append(decoder.decodeDoubleElement(descriptor, index))
+ }
+
+ override fun writeContent(encoder: CompositeEncoder, content: DoubleArray, size: Int) {
+ for (i in 0 until size)
+ encoder.encodeDoubleElement(descriptor, i, content[i])
+ }
+}
+
+@PublishedApi
+internal class DoubleArrayBuilder internal constructor(
+ bufferWithData: DoubleArray
+) : PrimitiveArrayBuilder<DoubleArray>() {
+
+ private var buffer: DoubleArray = bufferWithData
+ override var position: Int = bufferWithData.size
+ private set
+
+ init {
+ ensureCapacity(INITIAL_SIZE)
+ }
+
+ override fun ensureCapacity(requiredCapacity: Int) {
+ if (buffer.size < requiredCapacity)
+ buffer = buffer.copyOf(requiredCapacity.coerceAtLeast(buffer.size * 2))
+ }
+
+ internal fun append(c: Double) {
+ ensureCapacity()
+ buffer[position++] = c
+ }
+
+ override fun build() = buffer.copyOf(position)
+}
+
+/**
+ * Serializer for [CharArray].
+ *
+ * Encode elements one-by-one, as regular list,
+ * unless format's Encoder/Decoder have special handling for this serializer.
+ */
+@PublishedApi
+internal object CharArraySerializer : KSerializer<CharArray>,
+ PrimitiveArraySerializer<Char, CharArray, CharArrayBuilder>(Char.serializer()) {
+
+ override fun CharArray.collectionSize(): Int = size
+ override fun CharArray.toBuilder(): CharArrayBuilder = CharArrayBuilder(this)
+ override fun empty(): CharArray = CharArray(0)
+
+ override fun readElement(decoder: CompositeDecoder, index: Int, builder: CharArrayBuilder, checkIndex: Boolean) {
+ builder.append(decoder.decodeCharElement(descriptor, index))
+ }
+
+ override fun writeContent(encoder: CompositeEncoder, content: CharArray, size: Int) {
+ for (i in 0 until size)
+ encoder.encodeCharElement(descriptor, i, content[i])
+ }
+}
+
+@PublishedApi
+internal class CharArrayBuilder internal constructor(
+ bufferWithData: CharArray
+) : PrimitiveArrayBuilder<CharArray>() {
+
+ private var buffer: CharArray = bufferWithData
+ override var position: Int = bufferWithData.size
+ private set
+
+ init {
+ ensureCapacity(INITIAL_SIZE)
+ }
+
+ override fun ensureCapacity(requiredCapacity: Int) {
+ if (buffer.size < requiredCapacity)
+ buffer = buffer.copyOf(requiredCapacity.coerceAtLeast(buffer.size * 2))
+ }
+
+ internal fun append(c: Char) {
+ ensureCapacity()
+ buffer[position++] = c
+ }
+
+ override fun build() = buffer.copyOf(position)
+}
+
+/**
+ * Serializer for [BooleanArray].
+ * Encode elements one-by-one, as regular list, unless format's Encoder/Decoder have a special support for this serializer.
+ */
+@PublishedApi
+internal object BooleanArraySerializer : KSerializer<BooleanArray>,
+ PrimitiveArraySerializer<Boolean, BooleanArray, BooleanArrayBuilder>(Boolean.serializer()) {
+
+ override fun BooleanArray.collectionSize(): Int = size
+ override fun BooleanArray.toBuilder(): BooleanArrayBuilder = BooleanArrayBuilder(this)
+ override fun empty(): BooleanArray = BooleanArray(0)
+
+ override fun readElement(decoder: CompositeDecoder, index: Int, builder: BooleanArrayBuilder, checkIndex: Boolean) {
+ builder.append(decoder.decodeBooleanElement(descriptor, index))
+ }
+
+ override fun writeContent(encoder: CompositeEncoder, content: BooleanArray, size: Int) {
+ for (i in 0 until size)
+ encoder.encodeBooleanElement(descriptor, i, content[i])
+ }
+}
+
+@PublishedApi
+internal class BooleanArrayBuilder internal constructor(
+ bufferWithData: BooleanArray
+) : PrimitiveArrayBuilder<BooleanArray>() {
+
+ private var buffer: BooleanArray = bufferWithData
+ override var position: Int = bufferWithData.size
+ private set
+
+ init {
+ ensureCapacity(INITIAL_SIZE)
+ }
+
+ override fun ensureCapacity(requiredCapacity: Int) {
+ if (buffer.size < requiredCapacity)
+ buffer = buffer.copyOf(requiredCapacity.coerceAtLeast(buffer.size * 2))
+ }
+
+ internal fun append(c: Boolean) {
+ ensureCapacity()
+ buffer[position++] = c
+ }
+
+ override fun build() = buffer.copyOf(position)
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/Primitives.kt b/core/commonMain/src/kotlinx/serialization/internal/Primitives.kt
new file mode 100644
index 00000000..ab127ffa
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/Primitives.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("FunctionName")
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlin.native.concurrent.*
+import kotlin.reflect.*
+
+@SharedImmutable
+private val BUILTIN_SERIALIZERS = mapOf(
+ String::class to String.serializer(),
+ Char::class to Char.serializer(),
+ CharArray::class to CharArraySerializer(),
+ Double::class to Double.serializer(),
+ DoubleArray::class to DoubleArraySerializer(),
+ Float::class to Float.serializer(),
+ FloatArray::class to FloatArraySerializer(),
+ Long::class to Long.serializer(),
+ LongArray::class to LongArraySerializer(),
+ Int::class to Int.serializer(),
+ IntArray::class to IntArraySerializer(),
+ Short::class to Short.serializer(),
+ ShortArray::class to ShortArraySerializer(),
+ Byte::class to Byte.serializer(),
+ ByteArray::class to ByteArraySerializer(),
+ Boolean::class to Boolean.serializer(),
+ BooleanArray::class to BooleanArraySerializer(),
+ Unit::class to Unit.serializer()
+)
+
+internal class PrimitiveSerialDescriptor(
+ override val serialName: String,
+ override val kind: PrimitiveKind
+) : SerialDescriptor {
+ override val elementsCount: Int get() = 0
+ override fun getElementName(index: Int): String = error()
+ override fun getElementIndex(name: String): Int = error()
+ override fun isElementOptional(index: Int): Boolean = error()
+ override fun getElementDescriptor(index: Int): SerialDescriptor = error()
+ override fun getElementAnnotations(index: Int): List<Annotation> = error()
+ override fun toString(): String = "PrimitiveDescriptor($serialName)"
+ private fun error(): Nothing = throw IllegalStateException("Primitive descriptor does not have elements")
+}
+
+internal fun PrimitiveDescriptorSafe(serialName: String, kind: PrimitiveKind): SerialDescriptor {
+ checkName(serialName)
+ return PrimitiveSerialDescriptor(serialName, kind)
+}
+
+private fun checkName(serialName: String) {
+ val keys = BUILTIN_SERIALIZERS.keys
+ for (primitive in keys) {
+ val simpleName = primitive.simpleName!!.capitalize()
+ val qualifiedName = "kotlin.$simpleName" // KClass.qualifiedName is not supported in JS
+ if (serialName.equals(qualifiedName, ignoreCase = true) || serialName.equals(simpleName, ignoreCase = true)) {
+ throw IllegalArgumentException("""
+ The name of serial descriptor should uniquely identify associated serializer.
+ For serial name $serialName there already exist ${simpleName.capitalize()}Serializer.
+ Please refer to SerialDescriptor documentation for additional information.
+ """.trimIndent())
+ }
+ }
+}
+
+private fun String.capitalize() = replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
+
+@Suppress("UNCHECKED_CAST")
+internal fun <T : Any> KClass<T>.builtinSerializerOrNull(): KSerializer<T>? =
+ BUILTIN_SERIALIZERS[this] as KSerializer<T>?
+
+@PublishedApi
+internal object UnitSerializer : KSerializer<Unit> by ObjectSerializer("kotlin.Unit", Unit)
+
+@PublishedApi
+internal object BooleanSerializer : KSerializer<Boolean> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.Boolean", PrimitiveKind.BOOLEAN)
+ override fun serialize(encoder: Encoder, value: Boolean): Unit = encoder.encodeBoolean(value)
+ override fun deserialize(decoder: Decoder): Boolean = decoder.decodeBoolean()
+}
+
+@PublishedApi
+internal object ByteSerializer : KSerializer<Byte> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.Byte", PrimitiveKind.BYTE)
+ override fun serialize(encoder: Encoder, value: Byte): Unit = encoder.encodeByte(value)
+ override fun deserialize(decoder: Decoder): Byte = decoder.decodeByte()
+}
+
+@PublishedApi
+internal object ShortSerializer : KSerializer<Short> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.Short", PrimitiveKind.SHORT)
+ override fun serialize(encoder: Encoder, value: Short): Unit = encoder.encodeShort(value)
+ override fun deserialize(decoder: Decoder): Short = decoder.decodeShort()
+}
+
+@PublishedApi
+internal object IntSerializer : KSerializer<Int> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.Int", PrimitiveKind.INT)
+ override fun serialize(encoder: Encoder, value: Int): Unit = encoder.encodeInt(value)
+ override fun deserialize(decoder: Decoder): Int = decoder.decodeInt()
+}
+
+@PublishedApi
+internal object LongSerializer : KSerializer<Long> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.Long", PrimitiveKind.LONG)
+ override fun serialize(encoder: Encoder, value: Long): Unit = encoder.encodeLong(value)
+ override fun deserialize(decoder: Decoder): Long = decoder.decodeLong()
+}
+
+@PublishedApi
+internal object FloatSerializer : KSerializer<Float> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.Float", PrimitiveKind.FLOAT)
+ override fun serialize(encoder: Encoder, value: Float): Unit = encoder.encodeFloat(value)
+ override fun deserialize(decoder: Decoder): Float = decoder.decodeFloat()
+}
+
+@PublishedApi
+internal object DoubleSerializer : KSerializer<Double> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.Double", PrimitiveKind.DOUBLE)
+ override fun serialize(encoder: Encoder, value: Double): Unit = encoder.encodeDouble(value)
+ override fun deserialize(decoder: Decoder): Double = decoder.decodeDouble()
+}
+
+@PublishedApi
+internal object CharSerializer : KSerializer<Char> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.Char", PrimitiveKind.CHAR)
+ override fun serialize(encoder: Encoder, value: Char): Unit = encoder.encodeChar(value)
+ override fun deserialize(decoder: Decoder): Char = decoder.decodeChar()
+}
+
+@PublishedApi
+internal object StringSerializer : KSerializer<String> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.String", PrimitiveKind.STRING)
+ override fun serialize(encoder: Encoder, value: String): Unit = encoder.encodeString(value)
+ override fun deserialize(decoder: Decoder): String = decoder.decodeString()
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/SerializationConstructorMarker.kt b/core/commonMain/src/kotlinx/serialization/internal/SerializationConstructorMarker.kt
new file mode 100644
index 00000000..b6399c70
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/SerializationConstructorMarker.kt
@@ -0,0 +1,9 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+@Suppress("UNUSED")
+@Deprecated("Inserted into generated code and should not be used directly", level = DeprecationLevel.HIDDEN)
+public class SerializationConstructorMarker private constructor()
diff --git a/core/commonMain/src/kotlinx/serialization/internal/Tagged.kt b/core/commonMain/src/kotlinx/serialization/internal/Tagged.kt
new file mode 100644
index 00000000..e2e8d8f9
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/Tagged.kt
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+/*
+ * These classes are intended to be used only within the kotlinx.serialization.
+ * They neither do have stable API, nor internal invariants and are changed without any warnings.
+ */
+@InternalSerializationApi
+public abstract class TaggedEncoder<Tag : Any?> : Encoder, CompositeEncoder {
+
+ /**
+ * Provides a tag object for given serial descriptor and index.
+ * Tag object allows associating given user information with a particular element of composite serializable entity.
+ */
+ protected abstract fun SerialDescriptor.getTag(index: Int): Tag
+
+ override val serializersModule: SerializersModule
+ get() = EmptySerializersModule
+
+ // ---- API ----
+ protected open fun encodeTaggedValue(tag: Tag, value: Any): Unit =
+ throw SerializationException("Non-serializable ${value::class} is not supported by ${this::class} encoder")
+
+ protected open fun encodeTaggedNull(tag: Tag): Unit = throw SerializationException("null is not supported")
+ protected open fun encodeTaggedInt(tag: Tag, value: Int): Unit = encodeTaggedValue(tag, value)
+ protected open fun encodeTaggedByte(tag: Tag, value: Byte): Unit = encodeTaggedValue(tag, value)
+ protected open fun encodeTaggedShort(tag: Tag, value: Short): Unit = encodeTaggedValue(tag, value)
+ protected open fun encodeTaggedLong(tag: Tag, value: Long): Unit = encodeTaggedValue(tag, value)
+ protected open fun encodeTaggedFloat(tag: Tag, value: Float): Unit = encodeTaggedValue(tag, value)
+ protected open fun encodeTaggedDouble(tag: Tag, value: Double): Unit = encodeTaggedValue(tag, value)
+ protected open fun encodeTaggedBoolean(tag: Tag, value: Boolean): Unit = encodeTaggedValue(tag, value)
+ protected open fun encodeTaggedChar(tag: Tag, value: Char): Unit = encodeTaggedValue(tag, value)
+ protected open fun encodeTaggedString(tag: Tag, value: String): Unit = encodeTaggedValue(tag, value)
+
+ protected open fun encodeTaggedEnum(
+ tag: Tag,
+ enumDescriptor: SerialDescriptor,
+ ordinal: Int
+ ): Unit = encodeTaggedValue(tag, ordinal)
+
+ protected open fun encodeTaggedInline(tag: Tag, inlineDescriptor: SerialDescriptor): Encoder =
+ this.apply { pushTag(tag) }
+
+ final override fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder =
+ encodeTaggedInline(popTag(), inlineDescriptor)
+
+ // ---- Implementation of low-level API ----
+
+ private fun encodeElement(desc: SerialDescriptor, index: Int): Boolean {
+ val tag = desc.getTag(index)
+ pushTag(tag)
+ return true
+ }
+
+ final override fun encodeNotNullMark() {} // Does nothing, open because is not really required
+ open override fun encodeNull(): Unit = encodeTaggedNull(popTag())
+ final override fun encodeBoolean(value: Boolean): Unit = encodeTaggedBoolean(popTag(), value)
+ final override fun encodeByte(value: Byte): Unit = encodeTaggedByte(popTag(), value)
+ final override fun encodeShort(value: Short): Unit = encodeTaggedShort(popTag(), value)
+ final override fun encodeInt(value: Int): Unit = encodeTaggedInt(popTag(), value)
+ final override fun encodeLong(value: Long): Unit = encodeTaggedLong(popTag(), value)
+ final override fun encodeFloat(value: Float): Unit = encodeTaggedFloat(popTag(), value)
+ final override fun encodeDouble(value: Double): Unit = encodeTaggedDouble(popTag(), value)
+ final override fun encodeChar(value: Char): Unit = encodeTaggedChar(popTag(), value)
+ final override fun encodeString(value: String): Unit = encodeTaggedString(popTag(), value)
+
+ final override fun encodeEnum(
+ enumDescriptor: SerialDescriptor,
+ index: Int
+ ): Unit = encodeTaggedEnum(popTag(), enumDescriptor, index)
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder = this
+
+ final override fun endStructure(descriptor: SerialDescriptor) {
+ if (tagStack.isNotEmpty()) {
+ popTag()
+ }
+ endEncode(descriptor)
+ }
+
+ /**
+ * Format-specific replacement for [endStructure], because latter is overridden to manipulate tag stack.
+ */
+ protected open fun endEncode(descriptor: SerialDescriptor) {}
+
+ final override fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean): Unit =
+ encodeTaggedBoolean(descriptor.getTag(index), value)
+
+ final override fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte): Unit =
+ encodeTaggedByte(descriptor.getTag(index), value)
+
+ final override fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short): Unit =
+ encodeTaggedShort(descriptor.getTag(index), value)
+
+ final override fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int): Unit =
+ encodeTaggedInt(descriptor.getTag(index), value)
+
+ final override fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long): Unit =
+ encodeTaggedLong(descriptor.getTag(index), value)
+
+ final override fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float): Unit =
+ encodeTaggedFloat(descriptor.getTag(index), value)
+
+ final override fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double): Unit =
+ encodeTaggedDouble(descriptor.getTag(index), value)
+
+ final override fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char): Unit =
+ encodeTaggedChar(descriptor.getTag(index), value)
+
+ final override fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String): Unit =
+ encodeTaggedString(descriptor.getTag(index), value)
+
+ final override fun encodeInlineElement(
+ descriptor: SerialDescriptor,
+ index: Int
+ ): Encoder {
+ return encodeTaggedInline(descriptor.getTag(index), descriptor.getElementDescriptor(index))
+ }
+
+ override fun <T : Any?> encodeSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ serializer: SerializationStrategy<T>,
+ value: T
+ ) {
+ if (encodeElement(descriptor, index))
+ encodeSerializableValue(serializer, value)
+ }
+
+ @OptIn(ExperimentalSerializationApi::class)
+ override fun <T : Any> encodeNullableSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ serializer: SerializationStrategy<T>,
+ value: T?
+ ) {
+ if (encodeElement(descriptor, index))
+ encodeNullableSerializableValue(serializer, value)
+ }
+
+ private val tagStack = arrayListOf<Tag>()
+ protected val currentTag: Tag
+ get() = tagStack.last()
+ protected val currentTagOrNull: Tag?
+ get() = tagStack.lastOrNull()
+
+ protected fun pushTag(name: Tag) {
+ tagStack.add(name)
+ }
+
+ protected fun popTag(): Tag =
+ if (tagStack.isNotEmpty())
+ tagStack.removeAt(tagStack.lastIndex)
+ else
+ throw SerializationException("No tag in stack for requested element")
+}
+
+@InternalSerializationApi
+@OptIn(ExperimentalSerializationApi::class)
+public abstract class NamedValueEncoder : TaggedEncoder<String>() {
+ final override fun SerialDescriptor.getTag(index: Int): String = nested(elementName(this, index))
+ protected fun nested(nestedName: String): String = composeName(currentTagOrNull ?: "", nestedName)
+ protected open fun elementName(descriptor: SerialDescriptor, index: Int): String = descriptor.getElementName(index)
+ protected open fun composeName(parentName: String, childName: String): String =
+ if (parentName.isEmpty()) childName else "$parentName.$childName"
+}
+
+@InternalSerializationApi
+public abstract class TaggedDecoder<Tag : Any?> : Decoder, CompositeDecoder {
+ override val serializersModule: SerializersModule
+ get() = EmptySerializersModule
+
+ protected abstract fun SerialDescriptor.getTag(index: Int): Tag
+
+ // ---- API ----
+ protected open fun decodeTaggedValue(tag: Tag): Any =
+ throw SerializationException("${this::class} can't retrieve untyped values")
+
+ protected open fun decodeTaggedNotNullMark(tag: Tag): Boolean = true
+ protected open fun decodeTaggedNull(tag: Tag): Nothing? = null
+
+ protected open fun decodeTaggedBoolean(tag: Tag): Boolean = decodeTaggedValue(tag) as Boolean
+ protected open fun decodeTaggedByte(tag: Tag): Byte = decodeTaggedValue(tag) as Byte
+ protected open fun decodeTaggedShort(tag: Tag): Short = decodeTaggedValue(tag) as Short
+ protected open fun decodeTaggedInt(tag: Tag): Int = decodeTaggedValue(tag) as Int
+ protected open fun decodeTaggedLong(tag: Tag): Long = decodeTaggedValue(tag) as Long
+ protected open fun decodeTaggedFloat(tag: Tag): Float = decodeTaggedValue(tag) as Float
+ protected open fun decodeTaggedDouble(tag: Tag): Double = decodeTaggedValue(tag) as Double
+ protected open fun decodeTaggedChar(tag: Tag): Char = decodeTaggedValue(tag) as Char
+ protected open fun decodeTaggedString(tag: Tag): String = decodeTaggedValue(tag) as String
+ protected open fun decodeTaggedEnum(tag: Tag, enumDescriptor: SerialDescriptor): Int =
+ decodeTaggedValue(tag) as Int
+
+ protected open fun decodeTaggedInline(tag: Tag, inlineDescriptor: SerialDescriptor): Decoder = this.apply { pushTag(tag) }
+
+ protected open fun <T : Any?> decodeSerializableValue(deserializer: DeserializationStrategy<T>, previousValue: T?): T =
+ decodeSerializableValue(deserializer)
+
+
+ // ---- Implementation of low-level API ----
+
+ final override fun decodeInline(inlineDescriptor: SerialDescriptor): Decoder =
+ decodeTaggedInline(popTag(), inlineDescriptor)
+
+ // TODO this method should be overridden by any sane format that supports top-level nulls
+ override fun decodeNotNullMark(): Boolean {
+ // Tag might be null for top-level deserialization
+ val currentTag = currentTagOrNull ?: return false
+ return decodeTaggedNotNullMark(currentTag)
+ }
+
+ final override fun decodeNull(): Nothing? = null
+
+ final override fun decodeBoolean(): Boolean = decodeTaggedBoolean(popTag())
+ final override fun decodeByte(): Byte = decodeTaggedByte(popTag())
+ final override fun decodeShort(): Short = decodeTaggedShort(popTag())
+ final override fun decodeInt(): Int = decodeTaggedInt(popTag())
+ final override fun decodeLong(): Long = decodeTaggedLong(popTag())
+ final override fun decodeFloat(): Float = decodeTaggedFloat(popTag())
+ final override fun decodeDouble(): Double = decodeTaggedDouble(popTag())
+ final override fun decodeChar(): Char = decodeTaggedChar(popTag())
+ final override fun decodeString(): String = decodeTaggedString(popTag())
+
+ final override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeTaggedEnum(popTag(), enumDescriptor)
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder = this
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ // Nothing
+ }
+
+ final override fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean =
+ decodeTaggedBoolean(descriptor.getTag(index))
+
+ final override fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte =
+ decodeTaggedByte(descriptor.getTag(index))
+
+ final override fun decodeShortElement(descriptor: SerialDescriptor, index: Int): Short =
+ decodeTaggedShort(descriptor.getTag(index))
+
+ final override fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int =
+ decodeTaggedInt(descriptor.getTag(index))
+
+ final override fun decodeLongElement(descriptor: SerialDescriptor, index: Int): Long =
+ decodeTaggedLong(descriptor.getTag(index))
+
+ final override fun decodeFloatElement(descriptor: SerialDescriptor, index: Int): Float =
+ decodeTaggedFloat(descriptor.getTag(index))
+
+ final override fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int): Double =
+ decodeTaggedDouble(descriptor.getTag(index))
+
+ final override fun decodeCharElement(descriptor: SerialDescriptor, index: Int): Char =
+ decodeTaggedChar(descriptor.getTag(index))
+
+ final override fun decodeStringElement(descriptor: SerialDescriptor, index: Int): String =
+ decodeTaggedString(descriptor.getTag(index))
+
+ final override fun decodeInlineElement(
+ descriptor: SerialDescriptor,
+ index: Int
+ ): Decoder = decodeTaggedInline(descriptor.getTag(index), descriptor.getElementDescriptor(index))
+
+ final override fun <T : Any?> decodeSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ deserializer: DeserializationStrategy<T>,
+ previousValue: T?
+ ): T =
+ tagBlock(descriptor.getTag(index)) { decodeSerializableValue(deserializer, previousValue) }
+
+ final override fun <T : Any> decodeNullableSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ deserializer: DeserializationStrategy<T?>,
+ previousValue: T?
+ ): T? =
+ tagBlock(descriptor.getTag(index)) {
+ if (decodeNotNullMark()) decodeSerializableValue(
+ deserializer,
+ previousValue
+ ) else decodeNull()
+ }
+
+ private fun <E> tagBlock(tag: Tag, block: () -> E): E {
+ pushTag(tag)
+ val r = block()
+ if (!flag) {
+ popTag()
+ }
+ flag = false
+ return r
+ }
+
+ private val tagStack = arrayListOf<Tag>()
+ protected val currentTag: Tag
+ get() = tagStack.last()
+ protected val currentTagOrNull: Tag?
+ get() = tagStack.lastOrNull()
+
+ protected fun pushTag(name: Tag) {
+ tagStack.add(name)
+ }
+
+ protected fun copyTagsTo(other: TaggedDecoder<Tag>) {
+ other.tagStack.addAll(tagStack)
+ }
+
+ private var flag = false
+
+ protected fun popTag(): Tag {
+ val r = tagStack.removeAt(tagStack.lastIndex)
+ flag = true
+ return r
+ }
+}
+
+@InternalSerializationApi
+@OptIn(ExperimentalSerializationApi::class)
+public abstract class NamedValueDecoder : TaggedDecoder<String>() {
+ final override fun SerialDescriptor.getTag(index: Int): String = nested(elementName(this, index))
+
+ protected fun nested(nestedName: String): String = composeName(currentTagOrNull ?: "", nestedName)
+ protected open fun elementName(desc: SerialDescriptor, index: Int): String = desc.getElementName(index)
+ protected open fun composeName(parentName: String, childName: String): String =
+ if (parentName.isEmpty()) childName else "$parentName.$childName"
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/Tuples.kt b/core/commonMain/src/kotlinx/serialization/internal/Tuples.kt
new file mode 100644
index 00000000..1d30047e
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/internal/Tuples.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:Suppress("DEPRECATION_ERROR")
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlin.native.concurrent.*
+
+@SharedImmutable
+private val NULL = Any()
+private const val deprecationMessage =
+ "This class is used only by the plugin in generated code and should not be used directly. Use corresponding factory functions instead"
+
+@PublishedApi
+internal sealed class KeyValueSerializer<K, V, R>(
+ protected val keySerializer: KSerializer<K>,
+ protected val valueSerializer: KSerializer<V>
+) : KSerializer<R> {
+
+ protected abstract val R.key: K
+ protected abstract val R.value: V
+ protected abstract fun toResult(key: K, value: V): R
+
+ override fun serialize(encoder: Encoder, value: R) {
+ val structuredEncoder = encoder.beginStructure(descriptor)
+ structuredEncoder.encodeSerializableElement(descriptor, 0, keySerializer, value.key)
+ structuredEncoder.encodeSerializableElement(descriptor, 1, valueSerializer, value.value)
+ structuredEncoder.endStructure(descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): R {
+ val composite = decoder.beginStructure(descriptor)
+ if (composite.decodeSequentially()) {
+ val key = composite.decodeSerializableElement(descriptor, 0, keySerializer)
+ val value = composite.decodeSerializableElement(descriptor, 1, valueSerializer)
+ return toResult(key, value)
+ }
+
+ var key: Any? = NULL
+ var value: Any? = NULL
+ mainLoop@ while (true) {
+ when (val idx = composite.decodeElementIndex(descriptor)) {
+ CompositeDecoder.DECODE_DONE -> {
+ break@mainLoop
+ }
+ 0 -> {
+ key = composite.decodeSerializableElement(descriptor, 0, keySerializer)
+ }
+ 1 -> {
+ value = composite.decodeSerializableElement(descriptor, 1, valueSerializer)
+ }
+ else -> throw SerializationException("Invalid index: $idx")
+ }
+ }
+ composite.endStructure(descriptor)
+ if (key === NULL) throw SerializationException("Element 'key' is missing")
+ if (value === NULL) throw SerializationException("Element 'value' is missing")
+ @Suppress("UNCHECKED_CAST")
+ return toResult(key as K, value as V)
+ }
+}
+
+@PublishedApi
+@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
+internal class MapEntrySerializer<K, V>(
+ keySerializer: KSerializer<K>,
+ valueSerializer: KSerializer<V>
+) : KeyValueSerializer<K, V, Map.Entry<K, V>>(keySerializer, valueSerializer) {
+ private data class MapEntry<K, V>(override val key: K, override val value: V) : Map.Entry<K, V>
+
+ /*
+ * Kind 'MAP' because it is represented in a map-like manner with "key: value" serialized directly
+ */
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("kotlin.collections.Map.Entry", StructureKind.MAP) {
+ element("key", keySerializer.descriptor)
+ element("value", valueSerializer.descriptor)
+ }
+
+ override val Map.Entry<K, V>.key: K get() = this.key
+ override val Map.Entry<K, V>.value: V get() = this.value
+ override fun toResult(key: K, value: V): Map.Entry<K, V> = MapEntry(key, value)
+}
+
+@PublishedApi
+internal class PairSerializer<K, V>(
+ keySerializer: KSerializer<K>,
+ valueSerializer: KSerializer<V>
+) : KeyValueSerializer<K, V, Pair<K, V>>(keySerializer, valueSerializer) {
+ override val descriptor: SerialDescriptor = buildClassSerialDescriptor("kotlin.Pair") {
+ element("first", keySerializer.descriptor)
+ element("second", valueSerializer.descriptor)
+ }
+ override val Pair<K, V>.key: K get() = this.first
+ override val Pair<K, V>.value: V get() = this.second
+
+ override fun toResult(key: K, value: V): Pair<K, V> = key to value
+}
+
+
+@PublishedApi
+internal class TripleSerializer<A, B, C>(
+ private val aSerializer: KSerializer<A>,
+ private val bSerializer: KSerializer<B>,
+ private val cSerializer: KSerializer<C>
+) : KSerializer<Triple<A, B, C>> {
+
+ override val descriptor: SerialDescriptor = buildClassSerialDescriptor("kotlin.Triple") {
+ element("first", aSerializer.descriptor)
+ element("second", bSerializer.descriptor)
+ element("third", cSerializer.descriptor)
+ }
+
+ override fun serialize(encoder: Encoder, value: Triple<A, B, C>) {
+ val structuredEncoder = encoder.beginStructure(descriptor)
+ structuredEncoder.encodeSerializableElement(descriptor, 0, aSerializer, value.first)
+ structuredEncoder.encodeSerializableElement(descriptor, 1, bSerializer, value.second)
+ structuredEncoder.encodeSerializableElement(descriptor, 2, cSerializer, value.third)
+ structuredEncoder.endStructure(descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): Triple<A, B, C> {
+ val composite = decoder.beginStructure(descriptor)
+ if (composite.decodeSequentially()) {
+ return decodeSequentially(composite)
+ }
+ return decodeStructure(composite)
+ }
+
+ private fun decodeSequentially(composite: CompositeDecoder): Triple<A, B, C> {
+ val a = composite.decodeSerializableElement(descriptor, 0, aSerializer)
+ val b = composite.decodeSerializableElement(descriptor, 1, bSerializer)
+ val c = composite.decodeSerializableElement(descriptor, 2, cSerializer)
+ composite.endStructure(descriptor)
+ return Triple(a, b, c)
+ }
+
+ private fun decodeStructure(composite: CompositeDecoder): Triple<A, B, C> {
+ var a: Any? = NULL
+ var b: Any? = NULL
+ var c: Any? = NULL
+ mainLoop@ while (true) {
+ when (val index = composite.decodeElementIndex(descriptor)) {
+ CompositeDecoder.DECODE_DONE -> {
+ break@mainLoop
+ }
+ 0 -> {
+ a = composite.decodeSerializableElement(descriptor, 0, aSerializer)
+ }
+ 1 -> {
+ b = composite.decodeSerializableElement(descriptor, 1, bSerializer)
+ }
+ 2 -> {
+ c = composite.decodeSerializableElement(descriptor, 2, cSerializer)
+ }
+ else -> throw SerializationException("Unexpected index $index")
+ }
+ }
+ composite.endStructure(descriptor)
+ if (a === NULL) throw SerializationException("Element 'first' is missing")
+ if (b === NULL) throw SerializationException("Element 'second' is missing")
+ if (c === NULL) throw SerializationException("Element 'third' is missing")
+ @Suppress("UNCHECKED_CAST")
+ return Triple(a as A, b as B, c as C)
+ }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/modules/PolymorphicModuleBuilder.kt b/core/commonMain/src/kotlinx/serialization/modules/PolymorphicModuleBuilder.kt
new file mode 100644
index 00000000..31ce4574
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/modules/PolymorphicModuleBuilder.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.modules
+
+import kotlinx.serialization.*
+import kotlinx.serialization.internal.*
+import kotlin.reflect.*
+
+/**
+ * A builder which registers all its content for polymorphic serialization in the scope of the [base class][baseClass].
+ * If [baseSerializer] is present, registers it as a serializer for [baseClass] (which will be used if base class is serializable).
+ * Subclasses and its serializers can be added with [subclass] builder function.
+ *
+ * To obtain an instance of this builder, use [SerializersModuleBuilder.polymorphic] DSL function.
+ */
+public class PolymorphicModuleBuilder<in Base : Any> @PublishedApi internal constructor(
+ private val baseClass: KClass<Base>,
+ private val baseSerializer: KSerializer<Base>? = null
+) {
+ private val subclasses: MutableList<Pair<KClass<out Base>, KSerializer<out Base>>> = mutableListOf()
+ private var defaultSerializerProvider: ((Base) -> SerializationStrategy<Base>?)? = null
+ private var defaultDeserializerProvider: ((String?) -> DeserializationStrategy<out Base>?)? = null
+
+ /**
+ * Registers a [subclass] [serializer] in the resulting module under the [base class][Base].
+ */
+ public fun <T : Base> subclass(subclass: KClass<T>, serializer: KSerializer<T>) {
+ subclasses.add(subclass to serializer)
+ }
+
+ /**
+ * Adds a default serializers provider associated with the given [baseClass] to the resulting module.
+ * [defaultDeserializerProvider] is invoked when no polymorphic serializers associated with the `className`
+ * were found. `className` could be `null` for formats that support nullable class discriminators
+ * (currently only [Json] with [useArrayPolymorphism][JsonBuilder.useArrayPolymorphism] set to `false`)
+ *
+ * [defaultDeserializerProvider] can be stateful and lookup a serializer for the missing type dynamically.
+ *
+ * Typically, if the class is not registered in advance, it is not possible to know the structure of the unknown
+ * type and have a precise serializer, so the default serializer has limited capabilities.
+ * To have a structural access to the unknown data, it is recommended to use [JsonTransformingSerializer]
+ * or [JsonContentPolymorphicSerializer] classes.
+ *
+ * Default deserializers provider affects only deserialization process.
+ */
+ @ExperimentalSerializationApi
+ public fun defaultDeserializer(defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<out Base>?) {
+ require(this.defaultDeserializerProvider == null) {
+ "Default deserializer provider is already registered for class $baseClass: ${this.defaultDeserializerProvider}"
+ }
+ this.defaultDeserializerProvider = defaultDeserializerProvider
+ }
+
+ /**
+ * Adds a default deserializers provider associated with the given [baseClass] to the resulting module.
+ * [defaultSerializerProvider] is invoked when no polymorphic serializers associated with the `className`
+ * were found. `className` could be `null` for formats that support nullable class discriminators
+ * (currently only [Json] with [useArrayPolymorphism][JsonBuilder.useArrayPolymorphism] set to `false`)
+ *
+ * [defaultSerializerProvider] can be stateful and lookup a serializer for the missing type dynamically.
+ *
+ * [defaultSerializerProvider] is named as such for backwards compatibility reasons; it provides deserializers.
+ *
+ * Typically, if the class is not registered in advance, it is not possible to know the structure of the unknown
+ * type and have a precise serializer, so the default serializer has limited capabilities.
+ * To have a structural access to the unknown data, it is recommended to use [JsonTransformingSerializer]
+ * or [JsonContentPolymorphicSerializer] classes.
+ *
+ * Default deserializers provider affects only deserialization process. To affect serialization process, use
+ * [SerializersModuleBuilder.polymorphicDefaultSerializer].
+ *
+ * @see defaultDeserializer
+ */
+ @OptIn(ExperimentalSerializationApi::class)
+ // TODO: deprecate in 1.4
+ public fun default(defaultSerializerProvider: (className: String?) -> DeserializationStrategy<out Base>?) {
+ defaultDeserializer(defaultSerializerProvider)
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ @PublishedApi
+ internal fun buildTo(builder: SerializersModuleBuilder) {
+ if (baseSerializer != null) builder.registerPolymorphicSerializer(baseClass, baseClass, baseSerializer)
+ subclasses.forEach { (kclass, serializer) ->
+ builder.registerPolymorphicSerializer(
+ baseClass,
+ kclass as KClass<Base>,
+ serializer.cast()
+ )
+ }
+
+ val defaultSerializer = defaultSerializerProvider
+ if (defaultSerializer != null) {
+ builder.registerDefaultPolymorphicSerializer(baseClass, defaultSerializer, false)
+ }
+
+ val defaultDeserializer = defaultDeserializerProvider
+ if (defaultDeserializer != null) {
+ builder.registerDefaultPolymorphicDeserializer(baseClass, defaultDeserializer, false)
+ }
+ }
+}
+
+/**
+ * Registers a [subclass] [serializer] in the resulting module under the [base class][Base].
+ */
+public inline fun <Base : Any, reified T : Base> PolymorphicModuleBuilder<Base>.subclass(serializer: KSerializer<T>): Unit =
+ subclass(T::class, serializer)
+
+/**
+ * Registers a serializer for class [T] in the resulting module under the [base class][Base].
+ */
+public inline fun <Base : Any, reified T : Base> PolymorphicModuleBuilder<Base>.subclass(clazz: KClass<T>): Unit =
+ subclass(clazz, serializer())
diff --git a/core/commonMain/src/kotlinx/serialization/modules/SerializersModule.kt b/core/commonMain/src/kotlinx/serialization/modules/SerializersModule.kt
new file mode 100644
index 00000000..86f66d7a
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/modules/SerializersModule.kt
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.modules
+
+import kotlinx.serialization.*
+import kotlinx.serialization.internal.*
+import kotlin.jvm.*
+import kotlin.native.concurrent.*
+import kotlin.reflect.*
+
+/**
+ * [SerializersModule] is a collection of serializers used by [ContextualSerializer] and [PolymorphicSerializer]
+ * to override or provide serializers at the runtime, whereas at the compile-time they provided by the serialization plugin.
+ * It can be considered as a map where serializers can be found using their statically known KClasses.
+ *
+ * To enable runtime serializers resolution, one of the special annotations must be used on target types
+ * ([Polymorphic] or [Contextual]), and a serial module with serializers should be used during construction of [SerialFormat].
+ *
+ * @see Contextual
+ * @see Polymorphic
+ */
+public sealed class SerializersModule {
+
+ @ExperimentalSerializationApi
+ @Deprecated(
+ "Deprecated in favor of overload with default parameter",
+ ReplaceWith("getContextual(kclass)"),
+ DeprecationLevel.HIDDEN
+ ) // Was stable since 1.0.0, HIDDEN in 1.2.0 in a backwards-compatible manner
+ public fun <T : Any> getContextual(kclass: KClass<T>): KSerializer<T>? =
+ getContextual(kclass, emptyList())
+
+ /**
+ * Returns a contextual serializer associated with a given [kClass].
+ * If given class has generic parameters and module has provider for [kClass],
+ * [typeArgumentsSerializers] are used to create serializer.
+ * This method is used in context-sensitive operations on a property marked with [Contextual] by a [ContextualSerializer].
+ *
+ * @see SerializersModuleBuilder.contextual
+ */
+ @ExperimentalSerializationApi
+ public abstract fun <T : Any> getContextual(
+ kClass: KClass<T>,
+ typeArgumentsSerializers: List<KSerializer<*>> = emptyList()
+ ): KSerializer<T>?
+
+ /**
+ * Returns a polymorphic serializer registered for a class of the given [value] in the scope of [baseClass].
+ */
+ @ExperimentalSerializationApi
+ public abstract fun <T : Any> getPolymorphic(baseClass: KClass<in T>, value: T): SerializationStrategy<T>?
+
+ /**
+ * Returns a polymorphic deserializer registered for a [serializedClassName] in the scope of [baseClass]
+ * or default value constructed from [serializedClassName] if a default serializer provider was registered.
+ */
+ @ExperimentalSerializationApi
+ public abstract fun <T : Any> getPolymorphic(baseClass: KClass<in T>, serializedClassName: String?): DeserializationStrategy<out T>?
+
+ /**
+ * Copies contents of this module to the given [collector].
+ */
+ @ExperimentalSerializationApi
+ public abstract fun dumpTo(collector: SerializersModuleCollector)
+}
+
+/**
+ * A [SerializersModule] which is empty and always returns `null`.
+ */
+@SharedImmutable
+@ExperimentalSerializationApi
+public val EmptySerializersModule: SerializersModule = SerialModuleImpl(emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())
+
+/**
+ * Returns a combination of two serial modules
+ *
+ * If serializer for some class presents in both modules, a [SerializerAlreadyRegisteredException] is thrown.
+ * To overwrite serializers, use [SerializersModule.overwriteWith] function.
+ */
+public operator fun SerializersModule.plus(other: SerializersModule): SerializersModule = SerializersModule {
+ include(this@plus)
+ include(other)
+}
+
+/**
+ * Returns a combination of two serial modules
+ *
+ * If serializer for some class presents in both modules, result module
+ * will contain serializer from [other] module.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public infix fun SerializersModule.overwriteWith(other: SerializersModule): SerializersModule = SerializersModule {
+ include(this@overwriteWith)
+ other.dumpTo(object : SerializersModuleCollector {
+ override fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>) {
+ registerSerializer(kClass, ContextualProvider.Argless(serializer), allowOverwrite = true)
+ }
+
+ override fun <T : Any> contextual(
+ kClass: KClass<T>,
+ provider: (serializers: List<KSerializer<*>>) -> KSerializer<*>
+ ) {
+ registerSerializer(kClass, ContextualProvider.WithTypeArguments(provider), allowOverwrite = true)
+ }
+
+ override fun <Base : Any, Sub : Base> polymorphic(
+ baseClass: KClass<Base>,
+ actualClass: KClass<Sub>,
+ actualSerializer: KSerializer<Sub>
+ ) {
+ registerPolymorphicSerializer(baseClass, actualClass, actualSerializer, allowOverwrite = true)
+ }
+
+ override fun <Base : Any> polymorphicDefaultSerializer(
+ baseClass: KClass<Base>,
+ defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?
+ ) {
+ registerDefaultPolymorphicSerializer(baseClass, defaultSerializerProvider, allowOverwrite = true)
+ }
+
+ override fun <Base : Any> polymorphicDefaultDeserializer(
+ baseClass: KClass<Base>,
+ defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<out Base>?
+ ) {
+ registerDefaultPolymorphicDeserializer(baseClass, defaultDeserializerProvider, allowOverwrite = true)
+ }
+ })
+}
+
+// Implementation details below
+
+/**
+ * A default implementation of [SerializersModule]
+ * which uses hash maps to store serializers associated with KClasses.
+ */
+@Suppress("UNCHECKED_CAST")
+@OptIn(ExperimentalSerializationApi::class)
+internal class SerialModuleImpl(
+ private val class2ContextualFactory: Map<KClass<*>, ContextualProvider>,
+ @JvmField val polyBase2Serializers: Map<KClass<*>, Map<KClass<*>, KSerializer<*>>>,
+ private val polyBase2DefaultSerializerProvider: Map<KClass<*>, PolymorphicSerializerProvider<*>>,
+ private val polyBase2NamedSerializers: Map<KClass<*>, Map<String, KSerializer<*>>>,
+ private val polyBase2DefaultDeserializerProvider: Map<KClass<*>, PolymorphicDeserializerProvider<*>>
+) : SerializersModule() {
+
+ override fun <T : Any> getPolymorphic(baseClass: KClass<in T>, value: T): SerializationStrategy<T>? {
+ if (!value.isInstanceOf(baseClass)) return null
+ // Registered
+ val registered = polyBase2Serializers[baseClass]?.get(value::class) as? SerializationStrategy<T>
+ if (registered != null) return registered
+ // Default
+ return (polyBase2DefaultSerializerProvider[baseClass] as? PolymorphicSerializerProvider<T>)?.invoke(value)
+ }
+
+ override fun <T : Any> getPolymorphic(baseClass: KClass<in T>, serializedClassName: String?): DeserializationStrategy<out T>? {
+ // Registered
+ val registered = polyBase2NamedSerializers[baseClass]?.get(serializedClassName) as? KSerializer<out T>
+ if (registered != null) return registered
+ // Default
+ return (polyBase2DefaultDeserializerProvider[baseClass] as? PolymorphicDeserializerProvider<T>)?.invoke(serializedClassName)
+ }
+
+ override fun <T : Any> getContextual(kClass: KClass<T>, typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<T>? {
+ return (class2ContextualFactory[kClass]?.invoke(typeArgumentsSerializers)) as? KSerializer<T>?
+ }
+
+ override fun dumpTo(collector: SerializersModuleCollector) {
+ class2ContextualFactory.forEach { (kclass, serial) ->
+ when (serial) {
+ is ContextualProvider.Argless -> collector.contextual(
+ kclass as KClass<Any>,
+ serial.serializer as KSerializer<Any>
+ )
+ is ContextualProvider.WithTypeArguments -> collector.contextual(kclass, serial.provider)
+ }
+ }
+
+ polyBase2Serializers.forEach { (baseClass, classMap) ->
+ classMap.forEach { (actualClass, serializer) ->
+ collector.polymorphic(
+ baseClass as KClass<Any>,
+ actualClass as KClass<Any>,
+ serializer.cast()
+ )
+ }
+ }
+
+ polyBase2DefaultSerializerProvider.forEach { (baseClass, provider) ->
+ collector.polymorphicDefaultSerializer(baseClass as KClass<Any>, provider as (PolymorphicSerializerProvider<Any>))
+ }
+
+ polyBase2DefaultDeserializerProvider.forEach { (baseClass, provider) ->
+ collector.polymorphicDefaultDeserializer(baseClass as KClass<Any>, provider as (PolymorphicDeserializerProvider<out Any>))
+ }
+ }
+}
+
+internal typealias PolymorphicDeserializerProvider<Base> = (className: String?) -> DeserializationStrategy<out Base>?
+internal typealias PolymorphicSerializerProvider<Base> = (value: Base) -> SerializationStrategy<Base>?
+
+/** This class is needed to support re-registering the same static (argless) serializers:
+ *
+ * ```
+ * val m1 = serializersModuleOf(A::class, A.serializer())
+ * val m2 = serializersModuleOf(A::class, A.serializer())
+ * val aggregate = m1 + m2 // should not throw
+ * ```
+ */
+internal sealed class ContextualProvider {
+ abstract operator fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*>
+
+ class Argless(val serializer: KSerializer<*>) : ContextualProvider() {
+ override fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*> = serializer
+
+ override fun equals(other: Any?): Boolean = other is Argless && other.serializer == this.serializer
+
+ override fun hashCode(): Int = serializer.hashCode()
+ }
+
+ class WithTypeArguments(val provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>) :
+ ContextualProvider() {
+ override fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*> =
+ provider(typeArgumentsSerializers)
+ }
+
+}
diff --git a/core/commonMain/src/kotlinx/serialization/modules/SerializersModuleBuilders.kt b/core/commonMain/src/kotlinx/serialization/modules/SerializersModuleBuilders.kt
new file mode 100644
index 00000000..4c14f4b9
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/modules/SerializersModuleBuilders.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.serialization.modules
+
+import kotlinx.serialization.*
+import kotlinx.serialization.internal.*
+import kotlin.jvm.*
+import kotlin.reflect.*
+
+/**
+ * Returns a [SerializersModule] which has one class with one [serializer] for [ContextualSerializer].
+ */
+public fun <T : Any> serializersModuleOf(kClass: KClass<T>, serializer: KSerializer<T>): SerializersModule =
+ SerializersModule { contextual(kClass, serializer) }
+
+/**
+ * Returns a [SerializersModule] which has one class with one [serializer] for [ContextualSerializer].
+ */
+public inline fun <reified T : Any> serializersModuleOf(serializer: KSerializer<T>): SerializersModule =
+ serializersModuleOf(T::class, serializer)
+
+/**
+ * A builder function for creating a [SerializersModule].
+ * Serializers can be added via [SerializersModuleBuilder.contextual] or [SerializersModuleBuilder.polymorphic].
+ * Since [SerializersModuleBuilder] also implements [SerialModuleCollector],
+ * it is possible to copy whole another module to this builder with [SerializersModule.dumpTo]
+ */
+@Suppress("FunctionName")
+public inline fun SerializersModule(builderAction: SerializersModuleBuilder.() -> Unit): SerializersModule {
+ val builder = SerializersModuleBuilder()
+ builder.builderAction()
+ return builder.build()
+}
+
+/**
+ * A builder class for [SerializersModule] DSL. To create an instance of builder, use [SerializersModule] factory function.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public class SerializersModuleBuilder @PublishedApi internal constructor() : SerializersModuleCollector {
+ private val class2ContextualProvider: MutableMap<KClass<*>, ContextualProvider> = hashMapOf()
+ private val polyBase2Serializers: MutableMap<KClass<*>, MutableMap<KClass<*>, KSerializer<*>>> = hashMapOf()
+ private val polyBase2DefaultSerializerProvider: MutableMap<KClass<*>, PolymorphicSerializerProvider<*>> = hashMapOf()
+ private val polyBase2NamedSerializers: MutableMap<KClass<*>, MutableMap<String, KSerializer<*>>> = hashMapOf()
+ private val polyBase2DefaultDeserializerProvider: MutableMap<KClass<*>, PolymorphicDeserializerProvider<*>> = hashMapOf()
+
+ /**
+ * Adds [serializer] associated with given [kClass] for contextual serialization.
+ * If [kClass] has generic type parameters, consider registering provider instead.
+ *
+ * Throws [SerializationException] if a module already has serializer or provider associated with a [kClass].
+ * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used.
+ */
+ public override fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>): Unit =
+ registerSerializer(kClass, ContextualProvider.Argless(serializer))
+
+ /**
+ * Registers [provider] associated with given generic [kClass] for contextual serialization.
+ * When a serializer is requested from a module, provider is being called with type arguments serializers
+ * of the particular [kClass] usage.
+ *
+ * Example:
+ * ```
+ * class Holder(@Contextual val boxI: Box<Int>, @Contextual val boxS: Box<String>)
+ *
+ * val module = SerializersModule {
+ * // args[0] contains Int.serializer() or String.serializer(), depending on the property
+ * contextual(Box::class) { args -> BoxSerializer(args[0]) }
+ * }
+ * ```
+ *
+ * Throws [SerializationException] if a module already has provider or serializer associated with a [kClass].
+ * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used.
+ */
+ public override fun <T : Any> contextual(
+ kClass: KClass<T>,
+ provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>
+ ): Unit = registerSerializer(kClass, ContextualProvider.WithTypeArguments(provider))
+
+ /**
+ * Adds [serializer][actualSerializer] associated with given [actualClass] in the scope of [baseClass] for polymorphic serialization.
+ * Throws [SerializationException] if a module already has serializer associated with a [actualClass].
+ * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used.
+ */
+ public override fun <Base : Any, Sub : Base> polymorphic(
+ baseClass: KClass<Base>,
+ actualClass: KClass<Sub>,
+ actualSerializer: KSerializer<Sub>
+ ) {
+ registerPolymorphicSerializer(baseClass, actualClass, actualSerializer)
+ }
+
+ /**
+ * Adds a default serializers provider associated with the given [baseClass] to the resulting module.
+ * [defaultSerializerProvider] is invoked when no polymorphic serializers for `value` were found.
+ *
+ * This will not affect deserialization.
+ */
+ @ExperimentalSerializationApi
+ public override fun <Base : Any> polymorphicDefaultSerializer(
+ baseClass: KClass<Base>,
+ defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?
+ ) {
+ registerDefaultPolymorphicSerializer(baseClass, defaultSerializerProvider, false)
+ }
+
+ /**
+ * Adds a default deserializers provider associated with the given [baseClass] to the resulting module.
+ * [defaultDeserializerProvider] is invoked when no polymorphic serializers associated with the `className`
+ * were found. `className` could be `null` for formats that support nullable class discriminators
+ * (currently only `Json` with `useArrayPolymorphism` set to `false`).
+ *
+ * This will not affect serialization.
+ *
+ * @see PolymorphicModuleBuilder.defaultDeserializer
+ */
+ @ExperimentalSerializationApi
+ public override fun <Base : Any> polymorphicDefaultDeserializer(
+ baseClass: KClass<Base>,
+ defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<out Base>?
+ ) {
+ registerDefaultPolymorphicDeserializer(baseClass, defaultDeserializerProvider, false)
+ }
+
+ /**
+ * Copies the content of [module] module into the current builder.
+ */
+ public fun include(module: SerializersModule) {
+ module.dumpTo(this)
+ }
+
+ @JvmName("registerSerializer") // Don't mangle method name for prettier stack traces
+ internal fun <T : Any> registerSerializer(
+ forClass: KClass<T>,
+ provider: ContextualProvider,
+ allowOverwrite: Boolean = false
+ ) {
+ if (!allowOverwrite) {
+ val previous = class2ContextualProvider[forClass]
+ if (previous != null && previous != provider) {
+ // How can we provide meaningful name for WithTypeArgumentsProvider ?
+ throw SerializerAlreadyRegisteredException(
+ "Contextual serializer or serializer provider for $forClass already registered in this module"
+ )
+ }
+ }
+ class2ContextualProvider[forClass] = provider
+ }
+
+ @JvmName("registerDefaultPolymorphicSerializer") // Don't mangle method name for prettier stack traces
+ internal fun <Base : Any> registerDefaultPolymorphicSerializer(
+ baseClass: KClass<Base>,
+ defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?,
+ allowOverwrite: Boolean
+ ) {
+ val previous = polyBase2DefaultSerializerProvider[baseClass]
+ if (previous != null && previous != defaultSerializerProvider && !allowOverwrite) {
+ throw IllegalArgumentException("Default serializers provider for $baseClass is already registered: $previous")
+ }
+ polyBase2DefaultSerializerProvider[baseClass] = defaultSerializerProvider
+ }
+
+ @JvmName("registerDefaultPolymorphicDeserializer") // Don't mangle method name for prettier stack traces
+ internal fun <Base : Any> registerDefaultPolymorphicDeserializer(
+ baseClass: KClass<Base>,
+ defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<out Base>?,
+ allowOverwrite: Boolean
+ ) {
+ val previous = polyBase2DefaultDeserializerProvider[baseClass]
+ if (previous != null && previous != defaultDeserializerProvider && !allowOverwrite) {
+ throw IllegalArgumentException("Default deserializers provider for $baseClass is already registered: $previous")
+ }
+ polyBase2DefaultDeserializerProvider[baseClass] = defaultDeserializerProvider
+ }
+
+ @JvmName("registerPolymorphicSerializer") // Don't mangle method name for prettier stack traces
+ internal fun <Base : Any, Sub : Base> registerPolymorphicSerializer(
+ baseClass: KClass<Base>,
+ concreteClass: KClass<Sub>,
+ concreteSerializer: KSerializer<Sub>,
+ allowOverwrite: Boolean = false
+ ) {
+ // Check for overwrite
+ val name = concreteSerializer.descriptor.serialName
+ val baseClassSerializers = polyBase2Serializers.getOrPut(baseClass, ::hashMapOf)
+ val previousSerializer = baseClassSerializers[concreteClass]
+ val names = polyBase2NamedSerializers.getOrPut(baseClass, ::hashMapOf)
+ if (allowOverwrite) {
+ // Remove previous serializers from name mapping
+ if (previousSerializer != null) {
+ names.remove(previousSerializer.descriptor.serialName)
+ }
+ // Update mappings
+ baseClassSerializers[concreteClass] = concreteSerializer
+ names[name] = concreteSerializer
+ return
+ }
+ // Overwrite prohibited
+ if (previousSerializer != null) {
+ if (previousSerializer != concreteSerializer) {
+ throw SerializerAlreadyRegisteredException(baseClass, concreteClass)
+ } else {
+ // Cleanup name mapping
+ names.remove(previousSerializer.descriptor.serialName)
+ }
+ }
+ val previousByName = names[name]
+ if (previousByName != null) {
+ val conflictingClass = polyBase2Serializers[baseClass]!!.asSequence().find { it.value === previousByName }
+ throw IllegalArgumentException(
+ "Multiple polymorphic serializers for base class '$baseClass' " +
+ "have the same serial name '$name': '$concreteClass' and '$conflictingClass'"
+ )
+ }
+ // Overwrite if no conflicts
+ baseClassSerializers[concreteClass] = concreteSerializer
+ names[name] = concreteSerializer
+ }
+
+ @PublishedApi
+ internal fun build(): SerializersModule =
+ SerialModuleImpl(class2ContextualProvider, polyBase2Serializers, polyBase2DefaultSerializerProvider, polyBase2NamedSerializers, polyBase2DefaultDeserializerProvider)
+}
+
+/**
+ * Adds [serializer] associated with given type [T] for contextual serialization.
+ * Throws [SerializationException] if a module already has serializer associated with the given type.
+ * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used.
+ */
+public inline fun <reified T : Any> SerializersModuleBuilder.contextual(serializer: KSerializer<T>): Unit =
+ contextual(T::class, serializer)
+
+/**
+ * Creates a builder to register subclasses of a given [baseClass] for polymorphic serialization.
+ * If [baseSerializer] is not null, registers it as a serializer for [baseClass],
+ * which is useful if the base class is serializable itself. To register subclasses,
+ * [PolymorphicModuleBuilder.subclass] builder function can be used.
+ *
+ * If a serializer already registered for the given KClass in the given scope, an [IllegalArgumentException] is thrown.
+ * To override registered serializers, combine built module with another using [SerializersModule.overwriteWith].
+ *
+ * @see PolymorphicSerializer
+ */
+public inline fun <Base : Any> SerializersModuleBuilder.polymorphic(
+ baseClass: KClass<Base>,
+ baseSerializer: KSerializer<Base>? = null,
+ builderAction: PolymorphicModuleBuilder<Base>.() -> Unit = {}
+) {
+ val builder = PolymorphicModuleBuilder(baseClass, baseSerializer)
+ builder.builderAction()
+ builder.buildTo(this)
+}
+
+private class SerializerAlreadyRegisteredException internal constructor(msg: String) : IllegalArgumentException(msg) {
+ internal constructor(
+ baseClass: KClass<*>,
+ concreteClass: KClass<*>
+ ) : this("Serializer for $concreteClass already registered in the scope of $baseClass")
+}
diff --git a/core/commonMain/src/kotlinx/serialization/modules/SerializersModuleCollector.kt b/core/commonMain/src/kotlinx/serialization/modules/SerializersModuleCollector.kt
new file mode 100644
index 00000000..c4af77f8
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/modules/SerializersModuleCollector.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("RedundantVisibilityModifier")
+
+package kotlinx.serialization.modules
+
+import kotlinx.serialization.*
+import kotlin.reflect.*
+
+/**
+ * [SerializersModuleCollector] can introspect and accumulate content of any [SerializersModule] via [SerializersModule.dumpTo],
+ * using a visitor-like pattern: [contextual] and [polymorphic] functions are invoked for each registered serializer.
+ *
+ * ### Not stable for inheritance
+ *
+ * `SerializersModuleCollector` interface is not stable for inheritance in 3rd party libraries, as new methods
+ * might be added to this interface or contracts of the existing methods can be changed.
+ */
+@ExperimentalSerializationApi
+public interface SerializersModuleCollector {
+
+ /**
+ * Accept a serializer, associated with [kClass] for contextual serialization.
+ */
+ public fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>): Unit =
+ contextual(kClass) { serializer }
+
+
+ /**
+ * Accept a provider, associated with generic [kClass] for contextual serialization.
+ */
+ public fun <T : Any> contextual(
+ kClass: KClass<T>,
+ provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>
+ )
+
+ /**
+ * Accept a serializer, associated with [actualClass] for polymorphic serialization.
+ */
+ public fun <Base : Any, Sub : Base> polymorphic(
+ baseClass: KClass<Base>,
+ actualClass: KClass<Sub>,
+ actualSerializer: KSerializer<Sub>
+ )
+
+ /**
+ * Accept a default serializer provider, associated with the [baseClass] for polymorphic serialization.
+ *
+ * This will not affect deserialization.
+ *
+ * @see SerializersModuleBuilder.polymorphicDefaultSerializer
+ * @see PolymorphicModuleBuilder.defaultSerializer
+ */
+ @ExperimentalSerializationApi
+ public fun <Base : Any> polymorphicDefaultSerializer(
+ baseClass: KClass<Base>,
+ defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?
+ )
+
+ /**
+ * Accept a default deserializer provider, associated with the [baseClass] for polymorphic deserialization.
+ *
+ * This will not affect serialization.
+ *
+ * @see SerializersModuleBuilder.polymorphicDefaultDeserializer
+ * @see PolymorphicModuleBuilder.defaultDeserializer
+ */
+ @ExperimentalSerializationApi
+ public fun <Base : Any> polymorphicDefaultDeserializer(
+ baseClass: KClass<Base>,
+ defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<out Base>?
+ )
+
+ /**
+ * Accept a default deserializer provider, associated with the [baseClass] for polymorphic deserialization.
+ *
+ * This will not affect serialization.
+ *
+ * @see SerializersModuleBuilder.polymorphicDefaultDeserializer
+ * @see PolymorphicModuleBuilder.defaultDeserializer
+ */
+ // TODO: deprecate in 1.4
+ public fun <Base : Any> polymorphicDefault(
+ baseClass: KClass<Base>,
+ defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<out Base>?
+ ) {
+ polymorphicDefaultDeserializer(baseClass, defaultDeserializerProvider)
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/BasicTypesSerializationTest.kt b/core/commonTest/src/kotlinx/serialization/BasicTypesSerializationTest.kt
new file mode 100644
index 00000000..fab9702f
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/BasicTypesSerializationTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+/*
+ * Test ensures that type that aggregate all basic (primitive/collection/maps/arrays)
+ * types is properly serialized/deserialized with dummy format that supports only classes and primitives as
+ * first-class citizens.
+ */
+class BasicTypesSerializationTest {
+
+ // KeyValue Input/Output
+ class KeyValueOutput(private val sb: StringBuilder) : AbstractEncoder() {
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+ sb.append('{')
+ return this
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ sb.append('}')
+ }
+
+ override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
+ if (index > 0) sb.append(", ")
+ sb.append(descriptor.getElementName(index))
+ sb.append(':')
+ return true
+ }
+
+ override fun encodeNull() {
+ sb.append("null")
+ }
+
+ override fun encodeValue(value: Any) {
+ sb.append(value)
+ }
+
+ override fun encodeString(value: String) {
+ sb.append('"')
+ sb.append(value)
+ sb.append('"')
+ }
+
+ override fun encodeChar(value: Char) = encodeString(value.toString())
+ }
+
+ class KeyValueInput(private val inp: Parser) : AbstractDecoder() {
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ inp.expectAfterWhiteSpace('{')
+ return this
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) = inp.expectAfterWhiteSpace('}')
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ inp.skipWhitespace(',')
+ val name = inp.nextUntil(':', '}')
+ if (name.isEmpty())
+ return CompositeDecoder.DECODE_DONE
+ val index = descriptor.getElementIndex(name)
+ check(index != UNKNOWN_NAME)
+ inp.expect(':')
+ return index
+ }
+
+ private fun readToken(): String {
+ inp.skipWhitespace()
+ return inp.nextUntil(' ', ',', '}')
+ }
+
+ override fun decodeNotNullMark(): Boolean {
+ inp.skipWhitespace()
+ if (inp.cur != 'n'.code) return true
+ return false
+ }
+
+ override fun decodeNull(): Nothing? {
+ check(readToken() == "null") { "'null' expected" }
+ return null
+ }
+
+ override fun decodeBoolean(): Boolean = readToken() == "true"
+ override fun decodeByte(): Byte = readToken().toByte()
+ override fun decodeShort(): Short = readToken().toShort()
+ override fun decodeInt(): Int = readToken().toInt()
+ override fun decodeLong(): Long = readToken().toLong()
+ override fun decodeFloat(): Float = readToken().toFloat()
+ override fun decodeDouble(): Double = readToken().toDouble()
+
+ override fun decodeEnum(enumDescriptor: SerialDescriptor): Int {
+ return readToken().toInt()
+ }
+
+ override fun decodeString(): String {
+ inp.expectAfterWhiteSpace('"')
+ val value = inp.nextUntil('"')
+ inp.expect('"')
+ return value
+ }
+
+ override fun decodeChar(): Char = decodeString().single()
+ }
+
+ // Very simple char-by-char parser
+ class Parser(private val inp: StringReader) {
+ var cur: Int = inp.read()
+
+ fun next() {
+ cur = inp.read()
+ }
+
+ fun skipWhitespace(vararg c: Char) {
+ while (cur >= 0 && (cur.toChar().isWhitespace() || cur.toChar() in c))
+ next()
+ }
+
+ fun expect(c: Char) {
+ check(cur == c.code) { "Expected '$c'" }
+ next()
+ }
+
+ fun expectAfterWhiteSpace(c: Char) {
+ skipWhitespace()
+ expect(c)
+ }
+
+ fun nextUntil(vararg c: Char): String {
+ val sb = StringBuilder()
+ while (cur >= 0 && cur.toChar() !in c) {
+ sb.append(cur.toChar())
+ next()
+ }
+ return sb.toString()
+ }
+ }
+
+
+ class StringReader(val str: String) {
+ private var position: Int = 0
+ fun read(): Int = when (position) {
+ str.length -> -1
+ else -> str[position++].code
+ }
+ }
+
+ @Test
+ fun testKvSerialization() {
+ // serialize to string
+ val sb = StringBuilder()
+ val out = KeyValueOutput(sb)
+ out.encodeSerializableValue(TypesUmbrella.serializer(), umbrellaInstance)
+ // deserialize from string
+ val str = sb.toString()
+ val inp = KeyValueInput(Parser(StringReader(str)))
+ val other = inp.decodeSerializableValue(TypesUmbrella.serializer())
+ // assert we've got it back from string
+ assertEquals(umbrellaInstance, other)
+ assertNotSame(umbrellaInstance, other)
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt b/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt
new file mode 100644
index 00000000..d04390af
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.test.noJsLegacy
+import kotlin.test.*
+
+class CachedSerializersTest {
+ @Serializable
+ object Object
+
+ @Serializable
+ sealed class SealedParent {
+ @Serializable
+ data class Child(val i: Int) : SealedParent()
+ }
+
+ @Serializable
+ abstract class Abstract
+
+ @Test
+ fun testObjectSerializersAreSame() = noJsLegacy {
+ assertSame(Object.serializer(), Object.serializer())
+ }
+
+ @Test
+ fun testSealedSerializersAreSame() = noJsLegacy {
+ assertSame(SealedParent.serializer(), SealedParent.serializer())
+ }
+
+ @Test
+ fun testAbstractSerializersAreSame() = noJsLegacy {
+ assertSame(Abstract.serializer(), Abstract.serializer())
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/CustomPropertyAccessorsTest.kt b/core/commonTest/src/kotlinx/serialization/CustomPropertyAccessorsTest.kt
new file mode 100644
index 00000000..714c04c0
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/CustomPropertyAccessorsTest.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.AbstractDecoder
+import kotlinx.serialization.encoding.CompositeDecoder
+import kotlinx.serialization.modules.EmptySerializersModule
+import kotlinx.serialization.modules.SerializersModule
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFails
+
+@Suppress("RedundantSetter", "RedundantGetter", "JoinDeclarationAndAssignment")
+class CustomPropertyAccessorsTest {
+ @Serializable
+ class VarPropertiesClass {
+ var simple: String = "initial1"
+ var simpleInferred = "initial2"
+
+ var getterSetter: String = "initial3"
+ set(value) {
+ field = value
+ }
+ get() = field
+
+ var getterSetterInferred = "initial4"
+ set(value) {
+ field = value
+ }
+ get() {
+ return field
+ }
+
+ var setter: String = "initial5"
+ set(value) {
+ field = value
+ }
+
+ var deferredInit: String
+
+ var getterDeferredInit: String
+ get() {
+ return field
+ }
+
+
+ var noBackingField: String
+ set(value) {
+ println(value)
+ }
+ get() {
+ return "initial8"
+ }
+
+ init {
+ deferredInit = "initial6"
+ getterDeferredInit = "initial7"
+ }
+
+ }
+
+ @Serializable
+ class ValPropertiesClass {
+ val simple: String = "initial1"
+ val simpleInferred = "initial2"
+
+ val getter: String = "initial3"
+ get() {
+ return field
+ }
+
+ val getterInferred = "initial4"
+ get() {
+ return field
+ }
+
+ val deferredInit: String
+
+ val noBackingField: String
+ get() {
+ return "initial6"
+ }
+
+ init {
+ deferredInit = "initial5"
+ }
+ }
+
+ /*
+
+ This class can't be instantiated because it's hard to check val property with deferred init having backing
+ field on constructor resolve stage. So synthetic constructor's signature differs from it's body and it always
+ throw exception during call.
+ In IR back-end this class would not be compiled.
+ @Serializable
+ class BrokenValPropertiesClass {
+ private val getterDeferredInit: String
+ get() {
+ return field
+ }
+ init {
+ getterDeferredInit = "initial6"
+ }
+ }
+ */
+
+
+
+ private class CommonStringDecoder(private val elementCount: Int) : AbstractDecoder() {
+ override val serializersModule: SerializersModule = EmptySerializersModule
+ private var elementIndex = 0
+
+ override fun decodeString(): String {
+ return "decoded$elementIndex"
+ }
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == elementCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun decodeSequentially(): Boolean = false
+ }
+
+
+ @Test
+ fun testVarProperties() {
+ val varPropertiesClass = VarPropertiesClass.serializer().deserialize(CommonStringDecoder(7))
+
+ assertEquals("decoded1", varPropertiesClass.simple)
+ assertEquals("decoded2", varPropertiesClass.simpleInferred)
+ assertEquals("decoded3", varPropertiesClass.getterSetter)
+ assertEquals("decoded4", varPropertiesClass.getterSetterInferred)
+ assertEquals("decoded5", varPropertiesClass.setter)
+
+ // properties with deferred init always has value from init block - deserialize value ignored
+ assertEquals("initial6", varPropertiesClass.deferredInit)
+ assertEquals("initial7", varPropertiesClass.getterDeferredInit)
+ assertEquals("initial8", varPropertiesClass.noBackingField)
+ }
+
+ @Test
+ fun testValProperties() {
+ val valPropertiesClass = ValPropertiesClass.serializer().deserialize(CommonStringDecoder(5))
+
+ assertEquals("decoded1", valPropertiesClass.simple)
+ assertEquals("decoded2", valPropertiesClass.simpleInferred)
+ assertEquals("decoded3", valPropertiesClass.getter)
+ assertEquals("decoded4", valPropertiesClass.getterInferred)
+
+ // properties with deferred init always has value from init block - deserialize value ignored
+ assertEquals("initial5", valPropertiesClass.deferredInit)
+ assertEquals("initial6", valPropertiesClass.noBackingField)
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/ElementMarkerTest.kt b/core/commonTest/src/kotlinx/serialization/ElementMarkerTest.kt
new file mode 100644
index 00000000..a22be3ff
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/ElementMarkerTest.kt
@@ -0,0 +1,97 @@
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.CompositeDecoder
+import kotlinx.serialization.internal.ElementMarker
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ElementMarkerTest {
+ @Test
+ fun testNothingWasRead() {
+ val size = 5
+ val descriptor = createClassDescriptor(size)
+ val reader = ElementMarker(descriptor) { _, _ -> true }
+
+ for (i in 0 until size) {
+ assertEquals(i, reader.nextUnmarkedIndex())
+ }
+ assertEquals(CompositeDecoder.DECODE_DONE, reader.nextUnmarkedIndex())
+ }
+
+ @Test
+ fun testAllWasRead() {
+ val size = 5
+ val descriptor = createClassDescriptor(size)
+ val reader = ElementMarker(descriptor) { _, _ -> true }
+ for (i in 0 until size) {
+ reader.mark(i)
+ }
+
+ assertEquals(CompositeDecoder.DECODE_DONE, reader.nextUnmarkedIndex())
+ }
+
+ @Test
+ fun testFilteredRead() {
+ val size = 10
+ val readIndex = 4
+
+ val predicate: (Any?, Int) -> Boolean = { _, i -> i % 2 == 0 }
+ val descriptor = createClassDescriptor(size)
+ val reader = ElementMarker(descriptor, predicate)
+ reader.mark(readIndex)
+
+ for (i in 0 until size) {
+ if (predicate(descriptor, i) && i != readIndex) {
+ //`readIndex` already read and only filtered elements must be read
+ assertEquals(i, reader.nextUnmarkedIndex())
+ }
+ }
+ assertEquals(CompositeDecoder.DECODE_DONE, reader.nextUnmarkedIndex())
+ }
+
+ @Test
+ fun testSmallPartiallyRead() {
+ testPartiallyRead(Long.SIZE_BITS / 3)
+ }
+
+ @Test
+ fun test64PartiallyRead() {
+ testPartiallyRead(Long.SIZE_BITS)
+ }
+
+ @Test
+ fun test128PartiallyRead() {
+ testPartiallyRead(Long.SIZE_BITS * 2)
+ }
+
+ @Test
+ fun testLargePartiallyRead() {
+ testPartiallyRead(Long.SIZE_BITS * 2 + Long.SIZE_BITS / 3)
+ }
+
+ private fun testPartiallyRead(size: Int) {
+ val descriptor = createClassDescriptor(size)
+ val reader = ElementMarker(descriptor) { _, _ -> true }
+ for (i in 0 until size) {
+ if (i % 2 == 0) {
+ reader.mark(i)
+ }
+ }
+
+ for (i in 0 until size) {
+ if (i % 2 != 0) {
+ assertEquals(i, reader.nextUnmarkedIndex())
+ }
+ }
+ assertEquals(CompositeDecoder.DECODE_DONE, reader.nextUnmarkedIndex())
+ }
+
+ private fun createClassDescriptor(size: Int): SerialDescriptor {
+ return buildClassSerialDescriptor("descriptor") {
+ for (i in 0 until size) {
+ element("element$i", buildSerialDescriptor("int", PrimitiveKind.INT))
+ }
+ }
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/InheritableSerialInfoTest.kt b/core/commonTest/src/kotlinx/serialization/InheritableSerialInfoTest.kt
new file mode 100644
index 00000000..a117ad48
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/InheritableSerialInfoTest.kt
@@ -0,0 +1,48 @@
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.test.isJsLegacy
+import kotlin.test.*
+
+
+class InheritableSerialInfoTest {
+
+ @InheritableSerialInfo
+ annotation class InheritableDiscriminator(val discriminator: String)
+
+ @InheritableDiscriminator("a")
+ interface A
+
+ @InheritableDiscriminator("a")
+ interface B
+
+ @InheritableDiscriminator("a")
+ @Serializable
+ abstract class C: A
+
+ @Serializable
+ sealed class D: C(), B
+
+ @Serializable
+ class E: D()
+
+ @Serializable
+ class E2: C()
+
+ @Serializable
+ class E3: A, B
+
+ private fun doTest(descriptor: SerialDescriptor) {
+ if (isJsLegacy()) return // Unsupported
+ val list = descriptor.annotations.filterIsInstance<InheritableDiscriminator>()
+ assertEquals(1, list.size)
+ assertEquals("a", list.first().discriminator)
+ }
+
+ @Test
+ fun testInheritanceFromSealed() = doTest(E.serializer().descriptor)
+ @Test
+ fun testInheritanceFromAbstract() = doTest(E2.serializer().descriptor)
+ @Test
+ fun testInheritanceFromInterface() = doTest(E3.serializer().descriptor)
+}
diff --git a/core/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt b/core/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
new file mode 100644
index 00000000..393e2b59
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.modules.*
+import kotlin.native.concurrent.*
+
+@Serializable
+open class PolyBase(val id: Int) {
+ override fun hashCode(): Int {
+ return id
+ }
+
+ override fun toString(): String {
+ return "PolyBase(id=$id)"
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+ other as PolyBase
+ if (id != other.id) return false
+ return true
+ }
+
+}
+
+@Serializable
+data class PolyDerived(val s: String) : PolyBase(1)
+
+@SharedImmutable
+val BaseAndDerivedModule = SerializersModule {
+ polymorphic(PolyBase::class, PolyBase.serializer()) {
+ subclass(PolyDerived.serializer())
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/SealedGenericClassesTest.kt b/core/commonTest/src/kotlinx/serialization/SealedGenericClassesTest.kt
new file mode 100644
index 00000000..7840cd23
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/SealedGenericClassesTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.serializer
+import kotlinx.serialization.internal.UnitSerializer
+import kotlin.test.Test
+
+class SealedGenericClassesTest {
+ interface Output
+
+ @Serializable
+ data class Something(val s: String) : Output
+
+ @Serializable
+ sealed class Query<T : Output> {
+ @Serializable
+ data class SimpleQuery<T: Output>(val rawQuery: String) : Query<T>()
+ }
+
+ @Serializable
+ sealed class Fetcher<T : Output> {
+ abstract val query: Query<T>
+
+ @Serializable
+ data class SomethingFetcher(override val query: Query<Something>) : Fetcher<Something>()
+ }
+
+ // Test that compilation and retrieval is successful
+ @Test
+ fun testQuery() {
+ Query.SimpleQuery.serializer(String.serializer())
+ Query.serializer(UnitSerializer)
+ }
+
+ @Test
+ fun testFetcher() {
+ Fetcher.SomethingFetcher.serializer()
+ Fetcher.serializer(Something.serializer())
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/SerialDescriptorAnnotationsTest.kt b/core/commonTest/src/kotlinx/serialization/SerialDescriptorAnnotationsTest.kt
new file mode 100644
index 00000000..f2010a2d
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/SerialDescriptorAnnotationsTest.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.test.isJsLegacy
+import kotlin.test.*
+
+class SerialDescriptorAnnotationsTest {
+
+ @SerialInfo
+ @Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
+ annotation class CustomAnnotation(val value: String)
+
+ @Serializable
+ @SerialName("MyClass")
+ @CustomAnnotation("onClass")
+ data class WithNames(
+ val a: Int,
+ @CustomAnnotationWithDefault @CustomAnnotation("onProperty") val veryLongName: String
+ )
+
+ @SerialInfo
+ @Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
+ annotation class CustomAnnotationWithDefault(val value: String = "default_annotation_value")
+
+ @SerialInfo
+ @Target(AnnotationTarget.PROPERTY)
+ public annotation class JShort(val order: SByteOrder = SByteOrder.BE, val mod: SByteMod = SByteMod.Add)
+
+ public enum class SByteOrder {
+ BE, LE
+ }
+
+ public enum class SByteMod {
+ None, Add
+ }
+
+ @Serializable
+ public class Foo(
+ @JShort(SByteOrder.LE, SByteMod.None) public val bar: Short,
+ @JShort public val baz: Short
+ )
+
+
+ @Test
+ fun testSerialNameOnClass() {
+ val desc = WithNames.serializer().descriptor
+ val name = desc.serialName
+ assertEquals("MyClass", name)
+ }
+
+ @Test
+ fun testCustomSerialAnnotationOnProperty() {
+ val desc: SerialDescriptor = WithNames.serializer().descriptor
+ val b = desc.getElementAnnotations(1).getCustom()
+ assertEquals("onProperty", b)
+ }
+
+ @Test
+ fun testCustomSerialAnnotationOnClass() {
+ val desc: SerialDescriptor = WithNames.serializer().descriptor
+ val name = desc.annotations.getCustom()
+ assertEquals("onClass", name)
+ }
+
+ @Test
+ fun testCustomAnnotationWithDefaultValue() {
+ val value =
+ WithNames.serializer().descriptor
+ .getElementAnnotations(1).filterIsInstance<CustomAnnotationWithDefault>().single()
+ assertEquals("default_annotation_value", value.value)
+ }
+
+ @Test
+ fun testAnnotationWithMultipleArgs() {
+ fun SerialDescriptor.getValues(i: Int) = getElementAnnotations(i).filterIsInstance<JShort>().single().run { order to mod }
+ assertEquals(SByteOrder.LE to SByteMod.None, Foo.serializer().descriptor.getValues(0))
+ assertEquals(SByteOrder.BE to SByteMod.Add, Foo.serializer().descriptor.getValues(1))
+ }
+
+ private fun List<Annotation>.getCustom() = filterIsInstance<CustomAnnotation>().single().value
+
+ @Serializable
+ @CustomAnnotation("sealed")
+ sealed class Result {
+ @Serializable class OK(val s: String): Result()
+ }
+
+ @Serializable
+ @CustomAnnotation("abstract")
+ abstract class AbstractResult {
+ var result: String = ""
+ }
+
+ @Serializable
+ @CustomAnnotation("object")
+ object ObjectResult {}
+
+ @Serializable
+ class Holder(val r: Result, val a: AbstractResult, val o: ObjectResult, @Contextual val names: WithNames)
+
+ private fun doTest(position: Int, expected: String) {
+ if (isJsLegacy()) return // Unsupported
+ val desc = Holder.serializer().descriptor.getElementDescriptor(position)
+ assertEquals(expected, desc.annotations.getCustom())
+ }
+
+ @Test
+ fun testCustomAnnotationOnSealedClass() = doTest(0, "sealed")
+
+ @Test
+ fun testCustomAnnotationOnPolymorphicClass() = doTest(1, "abstract")
+
+ @Test
+ fun testCustomAnnotationOnObject() = doTest(2, "object")
+
+ @Test
+ fun testCustomAnnotationTransparentForContextual() = doTest(3, "onClass")
+}
diff --git a/core/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt b/core/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt
new file mode 100644
index 00000000..78b015b5
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class SerialDescriptorBuilderTest {
+
+ @Serializable
+ @SerialName("Wrapper")
+ class Wrapper(val i: Int)
+
+ private val wrapperDescriptor = buildClassSerialDescriptor("Wrapper") {
+ element("i", Int.serializer().descriptor)
+ }
+
+ private val dataHolderDescriptor = buildClassSerialDescriptor("DataHolder") {
+ element<String>("string")
+ element("nullableWrapper", wrapperDescriptor.nullable)
+ element("wrapper", wrapperDescriptor)
+ element<Int>("int")
+ element<Long?>("nullableOptionalLong", isOptional = true)
+ }
+
+ @Serializable
+ @SerialName("DataHolder")
+ class DataHolder(
+ val string: String,
+ val nullableWrapper: Wrapper?,
+ val wrapper: Wrapper,
+ val int: Int,
+ val nullableOptionalLong: Long? = null
+ )
+
+ @Test
+ fun testTrivialDescriptor() {
+ Wrapper.serializer().descriptor.assertDescriptorEqualsTo(wrapperDescriptor)
+ }
+
+
+ @Test
+ fun testNestedDescriptors() {
+ DataHolder.serializer().descriptor.assertDescriptorEqualsTo(dataHolderDescriptor)
+ }
+
+ @Serializable
+ @SerialName("Box")
+ class Box<T>(val value: T, val list: List<T>)
+
+ class CustomBoxSerializer<T>(val typeSerializer: KSerializer<T>) {
+ val descriptor: SerialDescriptor = buildClassSerialDescriptor("Box") {
+ element("value", typeSerializer.descriptor)
+ element("list", listSerialDescriptor(typeSerializer.descriptor))
+ }
+ }
+
+ @Test
+ fun testGenericDescriptor() {
+ val original = Box.serializer(Wrapper.serializer()).descriptor
+ val userDefined = CustomBoxSerializer(object : KSerializer<Wrapper> {
+ override val descriptor: SerialDescriptor = wrapperDescriptor
+ override fun serialize(encoder: Encoder, value: Wrapper) = TODO()
+ override fun deserialize(decoder: Decoder): Wrapper = TODO()
+ }).descriptor
+ original.assertDescriptorEqualsTo(userDefined)
+ }
+
+ @Test
+ fun testMisconfiguration() {
+ assertFailsWith<IllegalArgumentException> {
+ buildClassSerialDescriptor("a") {
+ element<Int>("i")
+ element<Int>("i")
+ }
+ }
+
+ assertFailsWith<IllegalArgumentException> { buildClassSerialDescriptor("") }
+ assertFailsWith<IllegalArgumentException> { buildClassSerialDescriptor("\t") }
+ assertFailsWith<IllegalArgumentException> { buildClassSerialDescriptor(" ") }
+ assertFailsWith<IllegalArgumentException> { PrimitiveSerialDescriptor("", PrimitiveKind.STRING) }
+ assertFailsWith<IllegalArgumentException> { PrimitiveSerialDescriptor(" ", PrimitiveKind.STRING) }
+ assertFailsWith<IllegalArgumentException> { PrimitiveSerialDescriptor("\t", PrimitiveKind.STRING) }
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/SerialDescriptorEqualityTest.kt b/core/commonTest/src/kotlinx/serialization/SerialDescriptorEqualityTest.kt
new file mode 100644
index 00000000..bd1962b7
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/SerialDescriptorEqualityTest.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlin.test.*
+
+class SerialDescriptorEqualityTest {
+ @Serializable
+ class TypeParamUsedOnce<T>(val t: T)
+
+ @Serializable
+ @SerialName("TypeParamUsedTwice")
+ class TypeParamUsedTwice<T>(val t: T, val l: List<T>)
+
+ @Serializable
+ class TypeParamInList<T>(val l: List<T>)
+
+ @Serializable
+ class RecursiveSimple(val desc: String, var node: RecursiveSimple?)
+
+ @Serializable
+ class Recursive<T>(val desc: T, var node: Recursive<T>?)
+
+ @Serializable
+ open class RecursiveGeneric<T : RecursiveGeneric<T>>()
+
+ @Serializable
+ class RecursiveGenericImpl(var other: RecursiveGeneric<RecursiveGenericImpl>? = null) :
+ RecursiveGeneric<RecursiveGenericImpl>()
+
+ private fun doTestWith(factory: (KSerializer<*>, KSerializer<*>) -> Pair<SerialDescriptor, SerialDescriptor>) {
+ val (a, b) = factory(Int.serializer(), Int.serializer())
+ assertEquals(a, b)
+ val (c, d) = factory(Int.serializer(), String.serializer())
+ assertNotEquals(c, d)
+ }
+
+ @Test
+ fun testUsedOnce() = doTestWith { d1, d2 ->
+ TypeParamUsedOnce.serializer(d1).descriptor to TypeParamUsedOnce.serializer(d2).descriptor
+ }
+
+ @Test
+ fun testUsedTwice() = doTestWith { d1, d2 ->
+ TypeParamUsedTwice.serializer(d1).descriptor to TypeParamUsedTwice.serializer(d2).descriptor
+ }
+
+ @Test
+ fun testUsedInList() = doTestWith { d1, d2 ->
+ TypeParamInList.serializer(d1).descriptor to TypeParamInList.serializer(d2).descriptor
+ }
+
+ @Test
+ fun testRecursive() = doTestWith { d1, d2 ->
+ Recursive.serializer(d1).descriptor to Recursive.serializer(d2).descriptor
+ }
+
+ @Test
+ fun testRecursiveSimple() {
+ val desc = RecursiveSimple.serializer().descriptor
+ assertEquals(desc, desc)
+ assertNotEquals(desc, Recursive.serializer(String.serializer()).descriptor)
+ }
+
+ @Test
+ fun testRecursiveGeneric() {
+ val descriptor1 = RecursiveGeneric.serializer(RecursiveGenericImpl.serializer()).descriptor
+ val descriptor2 = RecursiveGeneric.serializer(RecursiveGenericImpl.serializer()).descriptor
+ val descriptor3 =
+ RecursiveGeneric.serializer(RecursiveGeneric.serializer(RecursiveGenericImpl.serializer())).descriptor
+ assertNotEquals(descriptor1, descriptor3)
+ assertEquals(descriptor1, descriptor2)
+ }
+
+ @Test
+ fun testCantBeComparedToUserDescriptor() {
+ val typeParam = Int.serializer().descriptor
+ val userDefinedWithInt =
+ buildClassSerialDescriptor("TypeParamUsedTwice", typeParam) {
+ element("t", typeParam)
+ element("l", listSerialDescriptor(typeParam))
+ }
+
+ val generatedWithInt = TypeParamUsedTwice.serializer(Int.serializer()).descriptor
+ val generatedWithString = TypeParamUsedTwice.serializer(String.serializer()).descriptor
+ assertNotEquals(generatedWithInt, userDefinedWithInt)
+ assertNotEquals(generatedWithString, userDefinedWithInt)
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/SerialDescriptorSpecificationTest.kt b/core/commonTest/src/kotlinx/serialization/SerialDescriptorSpecificationTest.kt
new file mode 100644
index 00000000..4e888e85
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/SerialDescriptorSpecificationTest.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlin.test.*
+
+class SerialDescriptorSpecificationTest {
+
+ @Serializable
+ class Holder(val a: Int?, @Id(42) val b: String = "", @SerialName("c") val d: Long)
+
+ @Test
+ fun testAutoGeneratedDescriptorContract() {
+ testHolderDescriptor(Holder.serializer().descriptor)
+ }
+
+ @Test
+ fun testManuallyConstructedDescriptor() {
+ testHolderDescriptor(HolderDescriptor)
+ }
+
+ private object StaticHolder {
+ val userDefinedHolderDescriptor =
+ buildClassSerialDescriptor("kotlinx.serialization.SerialDescriptorSpecificationTest.Holder") {
+ element<Int?>("a")
+ val annotation = Holder.serializer().descriptor.findAnnotation<Id>(1)
+ element<String>("b", listOf(annotation!!), isOptional = true)
+ element<Long>("c")
+ }
+ }
+
+ private object HolderDescriptor : SerialDescriptor by StaticHolder.userDefinedHolderDescriptor
+
+ private fun testHolderDescriptor(d: SerialDescriptor) {
+ assertEquals(StructureKind.CLASS, d.kind)
+ assertEquals("kotlinx.serialization.SerialDescriptorSpecificationTest.Holder", d.serialName)
+ assertEquals(3, d.elementsCount)
+ // Indices
+ run {
+ assertEquals(0, d.getElementIndex("a"))
+ assertEquals(1, d.getElementIndex("b"))
+ assertEquals(2, d.getElementIndex("c"))
+ assertEquals(UNKNOWN_NAME, d.getElementIndex("?"))
+ assertEquals(UNKNOWN_NAME, d.getElementIndex("d"))
+
+ assertEquals("a", d.getElementName(0))
+ assertEquals("b", d.getElementName(1))
+ assertEquals("c", d.getElementName(2))
+ }
+ // Children annotations
+ run {
+ assertEquals(0, d.getElementAnnotations(0).size)
+ d.assertSingleAnnotation(1) { it is Id && it.id == 42 }
+ assertEquals(0, d.getElementAnnotations(2).size)
+ }
+ // Optionality
+ run {
+ assertFalse(d.isElementOptional(0))
+ assertTrue(d.isElementOptional(1))
+ assertFalse(d.isElementOptional(2))
+ }
+ // Children descriptors
+ run {
+ val aDescriptor = d.getElementDescriptor(0)
+ assertTrue(aDescriptor.isNullable)
+ assertTrue(aDescriptor.kind is PrimitiveKind.INT)
+
+ val bDescriptor = d.getElementDescriptor(1)
+ assertFalse(bDescriptor.isNullable)
+ assertTrue(bDescriptor.kind is PrimitiveKind.STRING)
+
+ val cDescriptor = d.getElementDescriptor(2)
+ assertFalse(cDescriptor.isNullable)
+ assertTrue(cDescriptor.kind is PrimitiveKind.LONG)
+ }
+ // Failure modes
+ assertFailsWith<IndexOutOfBoundsException> { d.isElementOptional(3) }
+ assertFailsWith<IndexOutOfBoundsException> { d.isElementOptional(Int.MAX_VALUE) }
+ assertFailsWith<IndexOutOfBoundsException> { d.getElementAnnotations(3) }
+ assertFailsWith<IndexOutOfBoundsException> { d.getElementName(3) }
+ assertFailsWith<IndexOutOfBoundsException> { d.getElementAnnotations(3) }
+ }
+
+ @SerialName("Named")
+ @Serializable
+ @Suppress("UNUSED")
+ enum class NamedEnum {
+ @Id(42)
+ FIRST,
+ @SerialName("SECOND")
+ THIRD
+ }
+
+ @Test
+ fun testEnumDescriptor() {
+ fun SerialDescriptor.verifyEnumMember(name: String) {
+ assertEquals("Named.$name", serialName)
+ assertEquals(0, elementsCount)
+ assertEquals(StructureKind.OBJECT, kind)
+ }
+
+ val d = NamedEnum.serializer().descriptor
+ assertEquals(SerialKind.ENUM, d.kind)
+ assertEquals("Named", d.serialName)
+ assertEquals(2, d.elementsCount)
+ assertFalse(d.isNullable)
+ // Names
+ assertEquals(0, d.getElementIndex("FIRST"))
+ assertEquals(1, d.getElementIndex("SECOND"))
+ assertEquals(UNKNOWN_NAME, d.getElementIndex("THIRD"))
+ // Elements
+ assertEquals("FIRST", d.getElementName(0))
+ assertEquals("SECOND", d.getElementName(1))
+ assertFailsWith<IndexOutOfBoundsException> { d.getElementName(2) }
+ // Annotations
+ d.assertSingleAnnotation(0) { it is Id && it.id == 42 }
+ assertEquals(0, d.getElementAnnotations(1).size)
+ // Element descriptors
+ d.getElementDescriptor(0).verifyEnumMember("FIRST")
+ d.getElementDescriptor(1).verifyEnumMember("SECOND")
+ assertFailsWith<IndexOutOfBoundsException> { d.getElementDescriptor(2) }
+ }
+
+ @Test
+ fun testListDescriptor() {
+ val descriptor = serializer<List<Int>>().descriptor
+ assertEquals("kotlin.collections.ArrayList", descriptor.serialName)
+ assertFalse(descriptor.isNullable)
+ assertEquals(1, descriptor.elementsCount)
+ assertSame(Int.serializer().descriptor, descriptor.getElementDescriptor(0))
+ assertSame(Int.serializer().descriptor, descriptor.getElementDescriptor(1))
+ assertFalse(descriptor.isElementOptional(0))
+ assertFalse(descriptor.isElementOptional(1))
+ assertFailsWith<IllegalArgumentException> { descriptor.isElementOptional(-1) }
+ }
+
+ @Test
+ fun testMapDescriptor() {
+ val descriptor = MapSerializer(
+ Int.serializer(),
+ Long.serializer()
+ ).descriptor
+ assertEquals("kotlin.collections.LinkedHashMap", descriptor.serialName)
+ assertFalse(descriptor.isNullable)
+ assertEquals(2, descriptor.elementsCount)
+ assertSame(Int.serializer().descriptor, descriptor.getElementDescriptor(0))
+ assertSame(Long.serializer().descriptor, descriptor.getElementDescriptor(1))
+ assertSame(Int.serializer().descriptor, descriptor.getElementDescriptor(2))
+ assertSame(Long.serializer().descriptor, descriptor.getElementDescriptor(3))
+ assertTrue(descriptor.getElementAnnotations(0).isEmpty())
+ assertTrue(descriptor.getElementAnnotations(1).isEmpty())
+ assertFalse(descriptor.isElementOptional(0))
+ assertFalse(descriptor.isElementOptional(1))
+ assertFalse(descriptor.isElementOptional(2))
+ assertFalse(descriptor.isElementOptional(3))
+ assertFailsWith<IllegalArgumentException> { descriptor.isElementOptional(-1) }
+ }
+
+ @Serializable
+ @SerialName("SerializableObject")
+ object SerializableObject {}
+
+ @Test
+ fun testObjectDescriptor() {
+ val descriptor = SerializableObject.serializer().descriptor
+ assertEquals(StructureKind.OBJECT, descriptor.kind)
+ assertEquals("SerializableObject", descriptor.serialName)
+ assertEquals(0, descriptor.elementsCount)
+ assertEquals(UNKNOWN_NAME, descriptor.getElementIndex("?"))
+ // Failure modes
+ assertFailsWith<IndexOutOfBoundsException> { descriptor.isElementOptional(0) }
+ assertFailsWith<IndexOutOfBoundsException> { descriptor.getElementAnnotations(0) }
+ assertFailsWith<IndexOutOfBoundsException> { descriptor.getElementName(0) }
+ assertFailsWith<IndexOutOfBoundsException> { descriptor.getElementAnnotations(0) }
+ }
+
+ @Test
+ fun testPrimitiveDescriptors() {
+ checkPrimitiveDescriptor("Int", Int.serializer().descriptor)
+ checkPrimitiveDescriptor("Boolean", Boolean.serializer().descriptor)
+ checkPrimitiveDescriptor("Byte", Byte.serializer().descriptor)
+ checkPrimitiveDescriptor("Short", Short.serializer().descriptor)
+ checkPrimitiveDescriptor("Long", Long.serializer().descriptor)
+ checkPrimitiveDescriptor("Float", Float.serializer().descriptor)
+ checkPrimitiveDescriptor("Double", Double.serializer().descriptor)
+ checkPrimitiveDescriptor("Char", Char.serializer().descriptor)
+ checkPrimitiveDescriptor("String", String.serializer().descriptor)
+ }
+
+ @Test
+ fun testUnitDescriptor() {
+ val descriptor = Unit.serializer().descriptor
+ assertEquals(StructureKind.OBJECT, descriptor.kind)
+ assertFalse(descriptor.isNullable)
+ assertEquals("kotlin.Unit", descriptor.serialName)
+ assertEquals(0, descriptor.annotations.size)
+ assertFailsWith<IndexOutOfBoundsException> { descriptor.getElementName(0) }
+ }
+
+ @Test
+ fun testCustomPrimitiveDescriptor() {
+ assertFailsWith<IllegalArgumentException> { PrimitiveSerialDescriptor("kotlin.Int", PrimitiveKind.INT) }
+ assertFailsWith<IllegalArgumentException> { PrimitiveSerialDescriptor("Int", PrimitiveKind.INT) }
+ assertFailsWith<IllegalArgumentException> { PrimitiveSerialDescriptor("int", PrimitiveKind.INT) }
+ }
+
+ private fun checkPrimitiveDescriptor(type: String, descriptor: SerialDescriptor) {
+ assertEquals(0, descriptor.elementsCount)
+ val kind = descriptor.kind.toString()
+ assertEquals(type.uppercase(), kind)
+ assertEquals("kotlin.${type}", descriptor.serialName)
+ assertEquals(0, descriptor.annotations.size)
+ assertFalse(descriptor.isNullable)
+ assertFailsWith<IllegalStateException> { descriptor.getElementName(0) }
+ }
+
+ inline fun SerialDescriptor.assertSingleAnnotation(index: Int, validator: (Annotation) -> Boolean) {
+ val annotations = getElementAnnotations(index)
+ assertEquals(1, annotations.size)
+ assertTrue(validator(annotations.first()))
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt b/core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt
new file mode 100644
index 00000000..4fb61b04
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+// This is unimplemented functionality that should be
+@Suppress("RemoveExplicitTypeArguments") // This is exactly what's being tested
+class SerializersLookupEnumTest {
+ @Serializable(with = EnumExternalObjectSerializer::class)
+ enum class EnumExternalObject
+
+ @Serializer(forClass = EnumExternalObject::class)
+ object EnumExternalObjectSerializer {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("tmp", SerialKind.ENUM)
+
+ override fun serialize(encoder: Encoder, value: EnumExternalObject) {
+ TODO()
+ }
+
+ override fun deserialize(decoder: Decoder): EnumExternalObject {
+ TODO()
+ }
+ }
+
+ @Serializable(with = EnumExternalClassSerializer::class)
+ enum class EnumExternalClass
+
+ @Serializer(forClass = EnumExternalClass::class)
+ class EnumExternalClassSerializer {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("tmp", SerialKind.ENUM)
+
+ override fun serialize(encoder: Encoder, value: EnumExternalClass) {
+ TODO()
+ }
+
+ override fun deserialize(decoder: Decoder): EnumExternalClass {
+ TODO()
+ }
+ }
+
+ @Polymorphic
+ enum class EnumPolymorphic
+
+ @Serializable
+ enum class PlainEnum
+
+ @Test
+ fun testPlainEnum() {
+ assertEquals(PlainEnum.serializer(), serializer<PlainEnum>())
+ }
+
+ @Test
+ fun testEnumExternalObject() {
+ assertSame(EnumExternalObjectSerializer, EnumExternalObject.serializer())
+ assertSame(EnumExternalObjectSerializer, serializer<EnumExternalObject>())
+ }
+
+ @Test
+ fun testEnumExternalClass() {
+ assertIs<EnumExternalClassSerializer>(EnumExternalClass.serializer())
+
+ if (isJvm()) {
+ assertIs<EnumExternalClassSerializer>(serializer<EnumExternalClass>())
+ } else if (isJsIr() || isNative()) {
+ // FIXME serializer<EnumWithClassSerializer> is broken for K/JS and K/Native. Remove `assertFails` after fix
+ assertFails { serializer<EnumExternalClass>() }
+ }
+ }
+
+ @Test
+ fun testEnumPolymorphic() {
+ if (isJvm()) {
+ assertEquals(
+ PolymorphicSerializer(EnumPolymorphic::class).descriptor,
+ serializer<EnumPolymorphic>().descriptor
+ )
+ } else {
+ // FIXME serializer<PolymorphicEnum> is broken for K/JS and K/Native. Remove `assertFails` after fix
+ assertFails { serializer<EnumPolymorphic>() }
+ }
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/SerializersLookupObjectTest.kt b/core/commonTest/src/kotlinx/serialization/SerializersLookupObjectTest.kt
new file mode 100644
index 00000000..49efb912
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/SerializersLookupObjectTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+@Suppress("RemoveExplicitTypeArguments") // This is exactly what's being tested
+class SerializersLookupObjectTest {
+ @Serializable(with = ObjectExternalObjectSerializer::class)
+ object ObjectExternalObject
+
+ @Serializer(forClass = ObjectExternalObject::class)
+ object ObjectExternalObjectSerializer {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("tmp", StructureKind.OBJECT)
+
+ override fun serialize(encoder: Encoder, value: ObjectExternalObject) {
+ TODO()
+ }
+
+ override fun deserialize(decoder: Decoder): ObjectExternalObject {
+ TODO()
+ }
+ }
+
+ @Serializable(with = ObjectExternalClassSerializer::class)
+ object ObjectExternalClass
+
+ @Serializer(forClass = ObjectExternalClass::class)
+ class ObjectExternalClassSerializer {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("tmp", StructureKind.OBJECT)
+
+ override fun serialize(encoder: Encoder, value: ObjectExternalClass) {
+ TODO()
+ }
+
+ override fun deserialize(decoder: Decoder): ObjectExternalClass {
+ TODO()
+ }
+ }
+
+ @Polymorphic
+ object ObjectPolymorphic
+
+ @Serializable
+ object PlainObject
+
+ @Test
+ fun testPlainObject() {
+ if (!isJsLegacy()) {
+ assertSame(PlainObject.serializer(), serializer<PlainObject>())
+ }
+ }
+
+
+ @Test
+ fun testObjectExternalObject() {
+ assertSame(ObjectExternalObjectSerializer, ObjectExternalObject.serializer())
+ if (!isJsLegacy()) {
+ assertSame(ObjectExternalObjectSerializer, serializer<ObjectExternalObject>())
+ }
+ }
+
+ @Test
+ fun testObjectExternalClass() {
+ assertIs<ObjectExternalClassSerializer>(ObjectExternalClass.serializer())
+
+ if (!isJsLegacy()) {
+ assertIs<ObjectExternalClassSerializer>(serializer<ObjectExternalClass>())
+ }
+ }
+
+ @Test
+ fun testEnumPolymorphic() {
+ if (isJvm()) {
+ assertEquals(
+ PolymorphicSerializer(ObjectPolymorphic::class).descriptor,
+ serializer<ObjectPolymorphic>().descriptor
+ )
+ } else {
+ // FIXME serializer<PolymorphicObject> is broken for K/JS and K/Native. Remove `assertFails` after fix
+ assertFails { serializer<ObjectPolymorphic>() }
+ }
+
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/TaggedTest.kt b/core/commonTest/src/kotlinx/serialization/TaggedTest.kt
new file mode 100644
index 00000000..6ae47cb6
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/TaggedTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+import kotlinx.serialization.internal.*
+
+class TaggedTest {
+
+ @Serializable
+ data class DataWithId(
+ @Id(1) val first: Int,
+ @Id(2) val second: String,
+ val noId: Unit = Unit,
+ @Id(42) val last: Boolean = true
+ )
+
+ class Collector : TaggedEncoder<Int?>() {
+ override fun SerialDescriptor.getTag(index: Int): Int? = getSerialId(this, index)
+ val tagList = mutableMapOf<Int?, Any>()
+ override fun encodeTaggedValue(tag: Int?, value: Any) {
+ tagList[tag] = value
+ }
+ }
+
+ class Emitter(private val collected: Collector) : TaggedDecoder<Int?>() {
+ private var i = 0
+
+ override fun decodeSequentially(): Boolean = true
+ override fun SerialDescriptor.getTag(index: Int): Int? = getSerialId(this, index)
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ // js doesn't generate code for .decodeSequentially for the sake of keeping output small
+ if (!isJs()) throw AssertionError("Should not be called in this test due to support of decodeSequentially")
+ return if (i == collected.tagList.size) CompositeDecoder.DECODE_DONE else i++
+ }
+
+ override fun decodeTaggedValue(tag: Int?): Any {
+ return collected.tagList.getValue(tag)
+ }
+ }
+
+ @Test
+ @Ignore
+ fun testTagged() {
+ val collector = Collector()
+ val data = DataWithId(1, "2")
+ collector.encodeSerializableValue(DataWithId.serializer(), data)
+ assertEquals(mapOf(1 to 1, 2 to "2", null to Unit, 42 to true), collector.tagList, "see all tags properly")
+ val obj = Emitter(collector).decodeSerializableValue(DataWithId.serializer())
+ assertEquals(obj, data, "read tags back")
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/TestId.kt b/core/commonTest/src/kotlinx/serialization/TestId.kt
new file mode 100644
index 00000000..eb759df8
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/TestId.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+
+@SerialInfo
+@Target(AnnotationTarget.PROPERTY)
+annotation class Id(val id: Int)
+
+public fun getSerialId(desc: SerialDescriptor, index: Int): Int?
+ = desc.findAnnotation<Id>(index)?.id
+
+public inline fun <reified A: Annotation> SerialDescriptor.findAnnotation(elementIndex: Int): A? {
+ val candidates = getElementAnnotations(elementIndex).filterIsInstance<A>()
+ return when (candidates.size) {
+ 0 -> null
+ 1 -> candidates[0]
+ else -> throw IllegalStateException("There are duplicate annotations of type ${A::class} in the descriptor $this")
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/UmbrellaTypes.kt b/core/commonTest/src/kotlinx/serialization/UmbrellaTypes.kt
new file mode 100644
index 00000000..61e42f61
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/UmbrellaTypes.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlin.native.concurrent.*
+
+@Serializable
+data class IntData(val intV: Int)
+
+enum class Attitude { POSITIVE, NEUTRAL, NEGATIVE }
+
+@Serializable
+data class Tree(val name: String, val left: Tree? = null, val right: Tree? = null)
+
+@Serializable
+data class TypesUmbrella(
+ val unit: Unit,
+ val boolean: Boolean,
+ val byte: Byte,
+ val short: Short,
+ val int: Int,
+ val long: Long,
+ val float: Float,
+ val double: Double,
+ val char: Char,
+ val string: String,
+ val enum: Attitude,
+ val intData: IntData,
+ val unitN: Unit?,
+ val booleanN: Boolean?,
+ val byteN: Byte?,
+ val shortN: Short?,
+ val intN: Int?,
+ val longN: Long?,
+ val floatN: Float?,
+ val doubleN: Double?,
+ val charN: Char?,
+ val stringN: String?,
+ val enumN: Attitude?,
+ val intDataN: IntData?,
+ val listInt: List<Int>,
+ val listIntN: List<Int?>,
+ val listNInt: Set<Int>?,
+ val listNIntN: MutableSet<Int?>?,
+ val listListEnumN: List<List<Attitude?>>,
+ val listIntData: List<IntData>,
+ val listIntDataN: MutableList<IntData?>,
+ val tree: Tree,
+ val mapStringInt: Map<String, Int>,
+ val mapIntStringN: Map<Int, String?>,
+ val arrays: ArraysUmbrella
+)
+
+@Serializable
+data class ArraysUmbrella(
+ val arrByte: Array<Byte>,
+ val arrInt: Array<Int>,
+ val arrIntN: Array<Int?>,
+ val arrIntData: Array<IntData>
+) {
+ override fun equals(other: Any?) = other is ArraysUmbrella &&
+ arrByte.contentEquals(other.arrByte) &&
+ arrInt.contentEquals(other.arrInt) &&
+ arrIntN.contentEquals(other.arrIntN) &&
+ arrIntData.contentEquals(other.arrIntData)
+}
+
+@SharedImmutable
+val umbrellaInstance = TypesUmbrella(
+ Unit, true, 10, 20, 30, 40, 50.1f, 60.1, 'A', "Str0", Attitude.POSITIVE, IntData(70),
+ null, null, 11, 21, 31, 41, 51.1f, 61.1, 'B', "Str1", Attitude.NEUTRAL, null,
+ listOf(1, 2, 3),
+ listOf(4, 5, null),
+ setOf(6, 7, 8),
+ mutableSetOf(null, 9, 10),
+ listOf(listOf(Attitude.NEGATIVE, null)),
+ listOf(IntData(1), IntData(2), IntData(3)),
+ mutableListOf(IntData(1), null, IntData(3)),
+ Tree("root", Tree("left"), Tree("right", Tree("right.left"), Tree("right.right"))),
+ mapOf("one" to 1, "two" to 2, "three" to 3),
+ mapOf(0 to null, 1 to "first", 2 to "second"),
+ ArraysUmbrella(
+ arrayOf(1, 2, 3),
+ arrayOf(100, 200, 300),
+ arrayOf(null, -1, -2),
+ arrayOf(IntData(1), IntData(2))
+ )
+)
diff --git a/core/commonTest/src/kotlinx/serialization/WrappedSerialDescriptorTest.kt b/core/commonTest/src/kotlinx/serialization/WrappedSerialDescriptorTest.kt
new file mode 100644
index 00000000..d92495b5
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/WrappedSerialDescriptorTest.kt
@@ -0,0 +1,61 @@
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class WrappedSerialDescriptorTest {
+
+ private fun checkWrapped(original: SerialDescriptor, wrappedName: String) {
+ val wrapped = SerialDescriptor(wrappedName, original)
+
+ assertEquals(wrappedName, wrapped.serialName)
+ assertNotEquals(original.serialName, wrapped.serialName)
+
+ assertEquals(original.elementsCount, wrapped.elementsCount)
+ assertEquals(original.isNullable, wrapped.isNullable)
+ assertEquals(original.annotations, wrapped.annotations)
+ assertEquals(original.kind, wrapped.kind)
+
+ for (i in 0 until original.elementsCount) {
+ original.getElementDescriptor(i).assertDescriptorEqualsTo(wrapped.getElementDescriptor(i))
+
+ assertEquals(original.getElementName(i), wrapped.getElementName(i))
+ assertEquals(original.getElementAnnotations(i), wrapped.getElementAnnotations(i))
+ assertEquals(original.isElementOptional(i), wrapped.isElementOptional(i))
+ }
+ }
+
+ @Test
+ fun testWrappedList() {
+ checkWrapped(ListSerializer(Int.serializer()).descriptor, "WrappedList")
+ }
+
+ @Test
+ fun testWrappedMap() {
+ checkWrapped(MapSerializer(String.serializer(), Int.serializer()).descriptor, "WrappedMap")
+ }
+
+ @Serializable
+ class SimpleType(val int: Int, val float: Float)
+
+ @Test
+ fun testWrappedSimpleClass() {
+ checkWrapped(SimpleType.serializer().descriptor, "WrappedSimpleType")
+ }
+
+ @Serializable
+ class ComplexType(
+ val string: String,
+ val nullableClass: SimpleType?,
+ val type: SimpleType,
+ val int: Int,
+ val nullableInt: Int?
+ )
+
+ @Test
+ fun testWrappedComplexClass() {
+ checkWrapped(ComplexType.serializer().descriptor, "WrappedComplexType")
+ }
+} \ No newline at end of file
diff --git a/core/commonTest/src/kotlinx/serialization/features/SchemaTest.kt b/core/commonTest/src/kotlinx/serialization/features/SchemaTest.kt
new file mode 100644
index 00000000..dece1704
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/features/SchemaTest.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.test.EnumSerializer
+import kotlin.test.*
+
+class SchemaTest {
+
+ enum class SampleEnum { OptionA, OptionB, OptionC }
+
+ @Serializable
+ data class Box<T>(val boxed: T)
+
+ @Serializable
+ data class Data1(val l: List<Int> = emptyList(), val s: String) {
+ @Serializer(forClass = Data1::class)
+ companion object {
+ // TODO removal of explicit type crashes the compiler
+ override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Data1") {
+ element("l", listSerialDescriptor<Int>(), isOptional = true)
+ element("s", serialDescriptor<String>())
+ }
+ }
+ }
+
+ @Serializable
+ data class Data2(val l: List<Int> = emptyList(), val s: String)
+
+ @Serializable data class BoxHolder(val stringBox: Box<String>, val intBox: Box<Int>)
+
+ @Serializable
+ data class DataZoo(
+ @Transient val invisible: String = "",
+ val a: Int,
+ val b: String,
+ val c: List<Data1>,
+ val ll: List<List<Boolean>>,
+ val m: Map<String, Data2>?
+ )
+
+ @Serializable
+ data class DataZooIsomorphic(
+ @Transient val invisible: String = "",
+ val b: Int,
+ val a: String,
+ val cc: List<Data1>,
+ val lll: List<List<Boolean>>,
+ val mm: Map<String, Data2>? = null
+ )
+
+ @Serializable
+ private data class DataWithEnum(val s: String, val enum: SampleEnum, val enumList: List<SampleEnum> = emptyList())
+
+ private fun checkDescriptor(serialDescriptor: SerialDescriptor) {
+ val nested = serialDescriptor.getElementDescriptor(0)
+ assertTrue(nested is ListLikeDescriptor)
+ val elem = nested.getElementDescriptor(0)
+ assertTrue(elem.kind is PrimitiveKind.INT)
+ assertEquals("kotlin.Int", elem.serialName)
+ assertTrue(serialDescriptor.isElementOptional(0))
+ }
+
+ @Test
+ fun testManualSchema() {
+ checkDescriptor(Data1.serializer().descriptor)
+ }
+
+ @Test
+ fun testGeneratedSchema() {
+ checkDescriptor(Data2.serializer().descriptor)
+ }
+
+ @Test
+ fun testRichSchema() {
+ val d: SerialDescriptor = DataZoo.serializer().descriptor
+ val descs = d.elementDescriptors.toList()
+ assertEquals(5, descs.size)
+ assertEquals(listOf(PrimitiveKind.INT, PrimitiveKind.STRING, StructureKind.LIST),
+ descs.take(3).map { it.kind })
+ val listListDesc = descs[3]
+ assertFalse(listListDesc.isNullable)
+ assertEquals(listListDesc.kind, StructureKind.LIST)
+ assertEquals(1, listListDesc.elementsCount)
+ assertEquals(PrimitiveKind.BOOLEAN, listListDesc.elementDescriptors.first().elementDescriptors.first().kind)
+ val mapDesc = descs[4]
+ assertTrue(mapDesc.isNullable)
+ assertFalse(d.isElementOptional(4), "Expected value to be marked as optional")
+ assertEquals(2, mapDesc.elementsCount)
+ assertEquals(listOf(PrimitiveKind.STRING, StructureKind.CLASS), mapDesc.kinds())
+ }
+
+ @Test
+ fun testEqualDescriptors() {
+ val desc1: SerialDescriptor = DataZoo.serializer().descriptor
+ val desc2: SerialDescriptor = DataZooIsomorphic.serializer().descriptor
+ assertEquals(desc1.elementDescriptors.toList(), desc2.elementDescriptors.toList())
+ assertEquals(Int.serializer().descriptor.elementDescriptors.toList(), Int.serializer().descriptor.elementDescriptors.toList())
+ }
+
+ @Test
+ fun testGenericDescriptors() {
+ val boxes = BoxHolder.serializer().descriptor.elementDescriptors.toList()
+ assertTrue(boxes[0].getElementDescriptor(0).kind is PrimitiveKind.STRING)
+ assertTrue(boxes[1].getElementDescriptor(0).kind is PrimitiveKind.INT)
+ assertNotEquals(boxes[0], boxes[1])
+ val intBox = Box.serializer(Int.serializer()).descriptor
+ assertEquals(intBox.kind, boxes[1].kind)
+ }
+
+ @Test
+ fun testEnumDescriptors() {
+ val dataDescriptor = DataWithEnum.serializer().descriptor
+ val enumDesc = dataDescriptor.getElementDescriptor(1)
+ val serialName = "kotlinx.serialization.features.SchemaTest.SampleEnum"
+ val manualSerializer = EnumSerializer<SampleEnum>(serialName)
+ assertEquals(enumDesc, manualSerializer.descriptor)
+ assertEquals(enumDesc, dataDescriptor.getElementDescriptor(2).getElementDescriptor(0))
+ }
+
+ @Test
+ fun testKindNames() {
+ val classDesc = BoxHolder.serializer().descriptor
+ assertEquals("CLASS", classDesc.kind.toString())
+ val intDesc = classDesc.elementDescriptors.toList()[1].elementDescriptors.toList()[0]
+ assertEquals("INT", intDesc.kind.toString())
+ }
+
+ private fun SerialDescriptor.kinds(): List<SerialKind> {
+ return List(elementsCount) { getElementDescriptor(it).kind }
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/modules/ContextualGenericsTest.kt b/core/commonTest/src/kotlinx/serialization/modules/ContextualGenericsTest.kt
new file mode 100644
index 00000000..cddb1cee
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/modules/ContextualGenericsTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.modules
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlin.test.*
+
+open class ContextualGenericsTest {
+ // This is a 3rd party class that we can't annotate as @Serializable
+ data class ThirdPartyBox<T>(val contents: T)
+
+ // This is the item that we put in the ThirdPartyBox, we control it, so can annotate it
+ @Serializable
+ data class Item(val name: String)
+
+ // This is the another item that we put in the ThirdPartyBox, we control it, so can annotate it
+ @Serializable
+ data class AnotherItem(val value: Int)
+
+ // The serializer for the ThirdPartyBox<T>
+ class ThirdPartyBoxSerializer<T>(dataSerializer: KSerializer<T>) : KSerializer<ThirdPartyBox<T>> {
+ @Serializable
+ data class BoxSurrogate<T>(val contents: T)
+
+ private val strategy = BoxSurrogate.serializer(dataSerializer)
+ override val descriptor: SerialDescriptor = strategy.descriptor
+
+ override fun deserialize(decoder: Decoder): ThirdPartyBox<T> {
+ return ThirdPartyBox(decoder.decodeSerializableValue(strategy).contents)
+ }
+
+ override fun serialize(encoder: Encoder, value: ThirdPartyBox<T>) {
+ encoder.encodeSerializableValue(strategy, BoxSurrogate(value.contents))
+ }
+ }
+
+ // Register contextual serializer for ThirdPartyBox<Item>
+ protected val boxWithItemSerializer = ThirdPartyBoxSerializer(Item.serializer())
+ protected val serializersModuleStatic = SerializersModule {
+ contextual(boxWithItemSerializer)
+ }
+
+ protected val serializersModuleWithProvider = SerializersModule {
+ contextual(ThirdPartyBox::class) { args -> ThirdPartyBoxSerializer(args[0]) }
+ }
+
+ @Test
+ fun testSurrogateSerializerFoundForGenericWithKotlinType() {
+ val serializer = serializersModuleStatic.serializer<ThirdPartyBox<Item>>()
+ assertEquals(boxWithItemSerializer.descriptor, serializer.descriptor)
+ }
+
+ @Test
+ fun testSerializerFoundForContextualGeneric() {
+ val serializerA = serializersModuleWithProvider.serializer<ThirdPartyBox<Item>>()
+ assertEquals(Item.serializer().descriptor, serializerA.descriptor.getElementDescriptor(0))
+ val serializerB = serializersModuleWithProvider.serializer<ThirdPartyBox<AnotherItem>>()
+ assertEquals(AnotherItem.serializer().descriptor, serializerB.descriptor.getElementDescriptor(0))
+ }
+
+ @Test
+ fun testModuleProvidesMultipleGenericSerializers() {
+ fun checkFor(serial: KSerializer<*>) {
+ val serializer = serializersModuleWithProvider.getContextual(ThirdPartyBox::class, listOf(serial))?.descriptor
+ assertEquals(serial.descriptor, serializer?.getElementDescriptor(0))
+ }
+ checkFor(Item.serializer())
+ checkFor(AnotherItem.serializer())
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/modules/ModuleBuildersTest.kt b/core/commonTest/src/kotlinx/serialization/modules/ModuleBuildersTest.kt
new file mode 100644
index 00000000..b4122cfd
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/modules/ModuleBuildersTest.kt
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("UNCHECKED_CAST")
+
+package kotlinx.serialization.modules
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlin.reflect.*
+import kotlin.test.*
+
+class ModuleBuildersTest {
+ @Serializable
+ class A(val i: Int)
+
+ @Serializable
+ class B(val b: String)
+
+ @Serializer(forClass = A::class)
+ object ASerializer : KSerializer<A>
+
+ @Serializer(forClass = B::class)
+ object BSerializer : KSerializer<B>
+
+ private fun SerializersModule.assertModuleHas(aSerializer: Boolean = false, bSerializer: Boolean = false) {
+ with(this) {
+ assertSame(if (aSerializer) ASerializer else null, getContextual(A::class))
+ assertSame(if (bSerializer) BSerializer else null, getContextual(B::class))
+ }
+ }
+
+ @Test
+ fun testSingletonModule() {
+ val module = serializersModuleOf(A::class, ASerializer)
+ module.assertModuleHas(
+ aSerializer = true,
+ bSerializer = false
+ )
+ }
+
+ @Test
+ fun testMapModule() {
+ val module1 = serializersModuleOf(BSerializer)
+ module1.assertModuleHas(
+ aSerializer = false,
+ bSerializer = true
+ )
+
+ SerializersModule {
+ contextual(ASerializer)
+ contextual(BSerializer)
+ }.assertModuleHas(
+ aSerializer = true,
+ bSerializer = true
+ )
+
+ (module1 + serializersModuleOf(A::class, ASerializer)).assertModuleHas(
+ aSerializer = true,
+ bSerializer = true
+ )
+ }
+
+ @Test
+ fun testCompositeModule() {
+ val moduleA = serializersModuleOf(ASerializer)
+ val moduleB = serializersModuleOf(BSerializer)
+
+ (moduleA + moduleB).assertModuleHas(
+ aSerializer = true,
+ bSerializer = true
+ )
+
+ var composite = SerializersModule { }
+ composite.assertModuleHas(
+ aSerializer = false,
+ bSerializer = false
+ )
+ composite += moduleA
+ composite.assertModuleHas(
+ aSerializer = true,
+ bSerializer = false
+ )
+ composite += moduleB
+ composite.assertModuleHas(
+ aSerializer = true,
+ bSerializer = true
+ )
+ }
+
+ @Test
+ fun testDSL() {
+ val module = SerializersModule {
+ contextual(A::class, ASerializer)
+ }
+ module.assertModuleHas(aSerializer = true, bSerializer = false)
+ }
+
+ @Test
+ fun testPolymorphicDSL() {
+ val module1 = SerializersModule {
+ polymorphic(PolyBase::class, PolyBase.serializer()) {
+ subclass(PolyDerived.serializer())
+ }
+ polymorphic(Any::class, baseSerializer = null) {
+ subclass(PolyBase.serializer())
+ subclass(PolyDerived.serializer())
+ }
+ }
+
+ val module2 = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(PolyBase::class)
+ subclass(PolyDerived.serializer())
+ }
+
+ polymorphic(PolyBase::class) {
+ subclass(PolyBase.serializer())
+ subclass(PolyDerived::class)
+ }
+ }
+
+ val base = PolyBase(10)
+ val derived = PolyDerived("foo")
+
+ listOf(module1, module2).forEachIndexed { index, module ->
+ fun <Base : Any, T : Base> assertPoly(serializer: KSerializer<T>, base: KClass<Base>, obj: T) =
+ assertEquals(
+ serializer,
+ module.getPolymorphic(base, obj),
+ "No serializer for ${obj::class} with base $base in module ${index + 1}:"
+ )
+
+ assertPoly(PolyBase.serializer(), PolyBase::class, base)
+ assertPoly(PolyDerived.serializer(), PolyBase::class, derived)
+ assertPoly(PolyBase.serializer(), Any::class, base)
+ assertPoly(PolyDerived.serializer(), Any::class, derived)
+ }
+
+ }
+
+ @Test
+ fun testOverwriteSerializer() {
+ val moduleA = SerializersModule {
+ contextual(A::class, ASerializer)
+ assertFailsWith<IllegalArgumentException> {
+ contextual(A::class, object : KSerializer<A> by A.serializer() {})
+ }
+ }
+ moduleA.assertModuleHas(aSerializer = true, bSerializer = false)
+ }
+
+ @Test
+ fun testOverwriteIsRightBiased() {
+ val incorrect = serializersModuleOf(A::class as KClass<Any>, BSerializer as KSerializer<Any>)
+ val correct = serializersModuleOf(ASerializer)
+ correct.assertModuleHas(aSerializer = true, bSerializer = false)
+ val sum = incorrect overwriteWith correct
+ sum.assertModuleHas(aSerializer = true, bSerializer = false)
+ }
+
+ @Test
+ fun testPlusThrowsExceptionOnDuplication() {
+ val incorrect = serializersModuleOf(A::class as KClass<Any>, BSerializer as KSerializer<Any>)
+ val correct = serializersModuleOf(ASerializer)
+ correct.assertModuleHas(aSerializer = true, bSerializer = false)
+ assertFailsWith<IllegalArgumentException> {
+ incorrect + correct
+ }
+ }
+
+ @Serializable
+ @SerialName("C")
+ class C
+
+ @Serializer(forClass = C::class)
+ object CSerializer : KSerializer<C> {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("AnotherName", StructureKind.OBJECT)
+ }
+
+ @Serializer(forClass = C::class)
+ object CSerializer2 : KSerializer<C> {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("C", StructureKind.OBJECT)
+ }
+
+ @Test
+ fun testOverwriteWithDifferentSerialName() {
+ val m1 = SerializersModule {
+ polymorphic<Any>(Any::class) {
+ subclass(C::class, CSerializer)
+ }
+ }
+ val m2 = SerializersModule {
+ polymorphic<Any>(Any::class) {
+ subclass(C::class, C.serializer())
+ }
+ }
+ assertEquals(CSerializer, m1.getPolymorphic(Any::class, serializedClassName = "AnotherName"))
+ assertFailsWith<IllegalArgumentException> { m1 + m2 }
+ val result = m1 overwriteWith m2
+ assertEquals(C.serializer(), result.getPolymorphic(Any::class, C()))
+ assertEquals(C.serializer(), result.getPolymorphic(Any::class, serializedClassName = "C"))
+ assertNull(result.getPolymorphic(Any::class, serializedClassName = "AnotherName"))
+ }
+
+ @Test
+ fun testOverwriteWithSameSerialName() {
+ val m1 = SerializersModule {
+ polymorphic<Any>(Any::class) {
+ subclass(C::class, C.serializer())
+ }
+ }
+ val m2 = SerializersModule {
+ polymorphic<Any>(Any::class) {
+ subclass(C::class, CSerializer2)
+ }
+ }
+ assertEquals(C.serializer(), m1.getPolymorphic(Any::class, serializedClassName = "C"))
+ assertEquals(CSerializer2, m2.getPolymorphic(Any::class, serializedClassName = "C"))
+ assertFailsWith<IllegalArgumentException> { m1 + m2 }
+ val result = m1 overwriteWith m2
+ assertEquals(CSerializer2, result.getPolymorphic(Any::class, C()))
+ assertEquals(CSerializer2, result.getPolymorphic(Any::class, serializedClassName = "C"))
+ }
+
+ @Test
+ fun testDoesntThrowOnTheSameSerializer() {
+ val m1 = serializersModuleOf(A::class, A.serializer())
+ val m2 = serializersModuleOf(A::class, A.serializer())
+ val aggregate = m1 + m2
+ assertEquals(A.serializer(), aggregate.getContextual(A::class))
+ }
+
+ @Test
+ fun testDoesntThrowOnTheEqualSerializers() {
+ val delegate = object : KSerializer<Unit> by Unit.serializer() {
+ override fun equals(other: Any?): Boolean = (other is KSerializer<*>) && other.descriptor == descriptor
+ }
+
+ val delegate2 = object : KSerializer<Unit> by Unit.serializer() {
+ override fun equals(other: Any?): Boolean = (other is KSerializer<*>) && other.descriptor == descriptor
+ }
+
+ val m1 = serializersModuleOf(Unit::class, delegate)
+ val m2 = serializersModuleOf(Unit::class, delegate2)
+ val aggregate = m1 + m2
+ assertEquals(delegate2, aggregate.getContextual(Unit::class))
+ assertEquals(delegate, aggregate.getContextual(Unit::class))
+ }
+
+ @Test
+ fun testThrowOnTheSamePolymorphicSerializer() {
+ val m1 = SerializersModule { polymorphic(Any::class) { subclass(A.serializer()) } }
+ val m2 = SerializersModule { polymorphic(Any::class) { subclass(ASerializer) } }
+ assertFailsWith<IllegalArgumentException> { m1 + m2 }
+ }
+
+ @Test
+ fun testDoesntThrowOnEqualPolymorphicSerializer() {
+ val delegate = object : KSerializer<Unit> by Unit.serializer() {
+ override fun equals(other: Any?): Boolean = (other is KSerializer<*>) && other.descriptor == descriptor
+ }
+
+ val delegate2 = object : KSerializer<Unit> by Unit.serializer() {
+ override fun equals(other: Any?): Boolean = (other is KSerializer<*>) && other.descriptor == descriptor
+ }
+
+ assertEquals(delegate as Any, delegate2 as Any)
+ val m1 = SerializersModule { polymorphic<Any>(Any::class) { subclass(delegate) } }
+ val m2 = SerializersModule { polymorphic<Any>(Any::class) { subclass(delegate2) } }
+ val aggregate = m1 + m2
+ assertEquals(delegate2, aggregate.getPolymorphic(Any::class, Unit))
+ assertEquals(delegate, aggregate.getPolymorphic(Any::class, Unit))
+ }
+
+ @Test
+ fun testPolymorphicCollision() {
+ val m1 = SerializersModule {
+ polymorphic<Any>(Any::class) {
+ defaultDeserializer { _ -> Unit.serializer() }
+ }
+ }
+
+ val m2 = SerializersModule {
+ polymorphic<Any>(Any::class) {
+ defaultDeserializer { _ -> Unit.serializer() }
+ }
+ }
+
+ assertFailsWith<IllegalArgumentException> { m1 + m2 }
+ }
+
+ @Test
+ fun testNoPolymorphicCollision() {
+ val defaultSerializerProvider = { _: String? -> Unit.serializer() }
+ val m1 = SerializersModule {
+ polymorphic(Any::class) {
+ defaultDeserializer(defaultSerializerProvider)
+ }
+ }
+
+ val m2 = m1 + m1
+ assertEquals<Any?>(Unit.serializer(), m2.getPolymorphic(Any::class, serializedClassName = "foo"))
+ }
+
+ @Test
+ fun testBothPolymorphicDefaults() {
+ val anySerializer = object : KSerializer<Any> {
+ override val descriptor: SerialDescriptor get() = error("descriptor")
+ override fun serialize(encoder: Encoder, value: Any): Unit = error("serialize")
+ override fun deserialize(decoder: Decoder): Any = error("deserialize")
+ }
+ val module = SerializersModule {
+ polymorphicDefaultDeserializer(Any::class) { _ -> anySerializer }
+ polymorphicDefaultSerializer(Any::class) { _ -> anySerializer }
+ }
+ assertEquals(anySerializer, module.getPolymorphic(Any::class, 42))
+ assertEquals(anySerializer, module.getPolymorphic(Any::class, serializedClassName = "42"))
+ }
+
+ @Test
+ fun testPolymorphicForStandardSubtypesOfAny() {
+ val serializer = object : KSerializer<Int> by Int.serializer() {}
+
+ val module = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(serializer)
+ }
+ }
+
+ assertSame(serializer, module.getPolymorphic(Any::class, 42))
+ assertSame(serializer, module.getPolymorphic(Any::class, serializedClassName = "kotlin.Int"))
+ }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt b/core/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt
new file mode 100644
index 00000000..c47252d7
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+enum class Platform {
+ JVM, JS_LEGACY, JS_IR, NATIVE
+}
+
+public expect val currentPlatform: Platform
+
+public fun isJs(): Boolean = currentPlatform == Platform.JS_LEGACY || currentPlatform == Platform.JS_IR
+public fun isJsLegacy(): Boolean = currentPlatform == Platform.JS_LEGACY
+public fun isJsIr(): Boolean = currentPlatform == Platform.JS_IR
+public fun isJvm(): Boolean = currentPlatform == Platform.JVM
+public fun isNative(): Boolean = currentPlatform == Platform.NATIVE
diff --git a/core/commonTest/src/kotlinx/serialization/test/TestHelpers.kt b/core/commonTest/src/kotlinx/serialization/test/TestHelpers.kt
new file mode 100644
index 00000000..660d1ef1
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/test/TestHelpers.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:Suppress("DEPRECATION_ERROR")
+package kotlinx.serialization.test
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.internal.EnumSerializer
+import kotlin.test.*
+
+@Suppress("TestFunctionName")
+internal inline fun <reified E : Enum<E>> EnumSerializer(serialName: String): EnumSerializer<E> =
+ EnumSerializer(serialName, enumValues())
+
+
+fun SerialDescriptor.assertDescriptorEqualsTo(other: SerialDescriptor) {
+ assertEquals(serialName, other.serialName)
+ assertEquals(elementsCount, other.elementsCount)
+ assertEquals(isNullable, other.isNullable)
+ assertEquals(annotations, other.annotations)
+ assertEquals(kind, other.kind)
+ for (i in 0 until elementsCount) {
+ getElementDescriptor(i).assertDescriptorEqualsTo(other.getElementDescriptor(i))
+ val name = getElementName(i)
+ val otherName = other.getElementName(i)
+ assertEquals(name, otherName)
+ assertEquals(getElementAnnotations(i), other.getElementAnnotations(i))
+ assertEquals(name, otherName)
+ assertEquals(isElementOptional(i), other.isElementOptional(i))
+ }
+}
+
+inline fun noJs(test: () -> Unit) {
+ if (!isJs()) test()
+}
+
+inline fun noJsLegacy(test: () -> Unit) {
+ if (!isJsLegacy()) test()
+}
+
+inline fun jvmOnly(test: () -> Unit) {
+ if (isJvm()) test()
+}
+
diff --git a/core/jsMain/src/kotlinx/serialization/Serializers.kt b/core/jsMain/src/kotlinx/serialization/Serializers.kt
new file mode 100644
index 00000000..6f7547db
--- /dev/null
+++ b/core/jsMain/src/kotlinx/serialization/Serializers.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlin.reflect.*
+
+
+@OptIn(ExperimentalAssociatedObjects::class)
+@AssociatedObjectKey
+@Retention(AnnotationRetention.BINARY)
+@PublishedApi
+internal annotation class SerializableWith(public val serializer: KClass<out KSerializer<*>>)
diff --git a/core/jsMain/src/kotlinx/serialization/internal/Platform.kt b/core/jsMain/src/kotlinx/serialization/internal/Platform.kt
new file mode 100644
index 00000000..25c48146
--- /dev/null
+++ b/core/jsMain/src/kotlinx/serialization/internal/Platform.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlin.reflect.*
+
+internal actual fun <T> Array<T>.getChecked(index: Int): T {
+ if (index !in indices) throw IndexOutOfBoundsException("Index $index out of bounds $indices")
+ return get(index)
+}
+
+internal actual fun BooleanArray.getChecked(index: Int): Boolean {
+ if (index !in indices) throw IndexOutOfBoundsException("Index $index out of bounds $indices")
+ return get(index)
+}
+@Suppress("UNCHECKED_CAST")
+internal actual fun <T : Any> KClass<T>.compiledSerializerImpl(): KSerializer<T>? =
+ this.constructSerializerForGivenTypeArgs() ?: this.js.asDynamic().Companion?.serializer() as? KSerializer<T>
+
+internal actual fun <T : Any, E : T?> ArrayList<E>.toNativeArrayImpl(eClass: KClass<T>): Array<E> = toTypedArray()
+
+internal actual fun Any.isInstanceOf(kclass: KClass<*>): Boolean = kclass.isInstance(this)
+
+internal actual fun KClass<*>.platformSpecificSerializerNotRegistered(): Nothing {
+ throw SerializationException(
+ "Serializer for class '${simpleName}' is not found.\n" +
+ "Mark the class as @Serializable or provide the serializer explicitly.\n" +
+ "On Kotlin/JS explicitly declared serializer should be used for interfaces and enums without @Serializable annotation"
+ )
+}
+
+@Suppress("UNCHECKED_CAST", "DEPRECATION_ERROR")
+@OptIn(ExperimentalAssociatedObjects::class)
+internal actual fun <T : Any> KClass<T>.constructSerializerForGivenTypeArgs(vararg args: KSerializer<Any?>): KSerializer<T>? =
+ try {
+ val assocObject = findAssociatedObject<SerializableWith>()
+ when {
+ assocObject is KSerializer<*> -> assocObject as KSerializer<T>
+ assocObject is SerializerFactory -> assocObject.serializer(*args) as KSerializer<T>
+ this.isInterface -> PolymorphicSerializer(this)
+ else -> null
+ }
+ } catch (e: dynamic) {
+ null
+ }
+
+internal actual fun isReferenceArray(rootClass: KClass<Any>): Boolean = rootClass == Array::class
+
+/**
+ * WARNING: may be broken in arbitrary time in the future without notice
+ *
+ * Should be eventually replaced with compiler intrinsics
+ */
+private val KClass<*>.isInterface
+ get(): Boolean = js.asDynamic().`$metadata$`?.kind == "interface"
diff --git a/core/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/core/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt
new file mode 100644
index 00000000..b87276e8
--- /dev/null
+++ b/core/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+public actual val currentPlatform: Platform = if (isLegacyBackend()) Platform.JS_LEGACY else Platform.JS_IR
+
+// from https://github.com/JetBrains/kotlin/blob/569187a7516e2e5ab217158a3170d4beb0c5cb5a/js/js.translator/testData/_commonFiles/testUtils.kt#L3
+private fun isLegacyBackend(): Boolean =
+ // Using eval to prevent DCE from thinking that following code depends on Kotlin module.
+ eval("(typeof Kotlin != \"undefined\" && typeof Kotlin.kotlin != \"undefined\")").unsafeCast<Boolean>()
diff --git a/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt b/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt
new file mode 100644
index 00000000..b110f121
--- /dev/null
+++ b/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:JvmMultifileClass
+@file:JvmName("SerializersKt")
+@file:Suppress("UNCHECKED_CAST")
+
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.builtins.MapEntrySerializer
+import kotlinx.serialization.builtins.PairSerializer
+import kotlinx.serialization.builtins.TripleSerializer
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.modules.*
+import java.lang.reflect.*
+import kotlin.reflect.*
+
+/**
+ * Reflectively constructs a serializer for the given reflective Java [type].
+ * [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit,
+ * that operate with reflective Java [Type] and cannot use [typeOf].
+ *
+ * For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of
+ * Kotlin-specific type information, such as nullability, sealed classes and object singletons.
+ *
+ * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable).
+ */
+@ExperimentalSerializationApi
+public fun serializer(type: Type): KSerializer<Any> = EmptySerializersModule.serializer(type)
+
+/**
+ * Reflectively constructs a serializer for the given reflective Java [type].
+ * [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit,
+ * that operate with reflective Java [Type] and cannot use [typeOf].
+ *
+ * For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of
+ * Kotlin-specific type information, such as nullability, sealed classes and object singletons.
+ *
+ * Returns `null` if serializer cannot be created (provided [type] or its type argument is not serializable).
+ */
+@ExperimentalSerializationApi
+public fun serializerOrNull(type: Type): KSerializer<Any>? = EmptySerializersModule.serializerOrNull(type)
+
+/**
+ * Retrieves serializer for the given reflective Java [type] using
+ * reflective construction and [contextual][SerializersModule.getContextual] lookup for non-serializable types.
+ *
+ * [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit,
+ * that operate with reflective Java [Type] and cannot use [typeOf].
+ *
+ * For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of
+ * Kotlin-specific type information, such as nullability, sealed classes and object singletons.
+ *
+ * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable).
+ */
+@ExperimentalSerializationApi
+public fun SerializersModule.serializer(type: Type): KSerializer<Any> =
+ serializerByJavaTypeImpl(type, failOnMissingTypeArgSerializer = true) ?: type.prettyClass().serializerNotRegistered()
+
+/**
+ * Retrieves serializer for the given reflective Java [type] using
+ * reflective construction and [contextual][SerializersModule.getContextual] lookup for non-serializable types.
+ *
+ * [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit,
+ * that operate with reflective Java [Type] and cannot use [typeOf].
+ *
+ * For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of
+ * Kotlin-specific type information, such as nullability, sealed classes and object singletons.
+ *
+ * Returns `null` if serializer cannot be created (provided [type] or its type argument is not serializable).
+ */
+@ExperimentalSerializationApi
+public fun SerializersModule.serializerOrNull(type: Type): KSerializer<Any>? =
+ serializerByJavaTypeImpl(type, failOnMissingTypeArgSerializer = false)
+
+@OptIn(ExperimentalSerializationApi::class)
+private fun SerializersModule.serializerByJavaTypeImpl(type: Type, failOnMissingTypeArgSerializer: Boolean = true): KSerializer<Any>? =
+ when (type) {
+ is GenericArrayType -> {
+ genericArraySerializer(type, failOnMissingTypeArgSerializer)
+ }
+ is Class<*> -> typeSerializer(type, failOnMissingTypeArgSerializer)
+ is ParameterizedType -> {
+ val rootClass = (type.rawType as Class<*>)
+ val args = (type.actualTypeArguments)
+ val argsSerializers =
+ if (failOnMissingTypeArgSerializer) args.map { serializer(it) } else args.map { serializerOrNull(it) ?: return null }
+ when {
+ Set::class.java.isAssignableFrom(rootClass) -> SetSerializer(argsSerializers[0]) as KSerializer<Any>
+ List::class.java.isAssignableFrom(rootClass) || Collection::class.java.isAssignableFrom(rootClass) -> ListSerializer(
+ argsSerializers[0]
+ ) as KSerializer<Any>
+ Map::class.java.isAssignableFrom(rootClass) -> MapSerializer(
+ argsSerializers[0],
+ argsSerializers[1]
+ ) as KSerializer<Any>
+ Map.Entry::class.java.isAssignableFrom(rootClass) -> MapEntrySerializer(
+ argsSerializers[0],
+ argsSerializers[1]
+ ) as KSerializer<Any>
+ Pair::class.java.isAssignableFrom(rootClass) -> PairSerializer(
+ argsSerializers[0],
+ argsSerializers[1]
+ ) as KSerializer<Any>
+ Triple::class.java.isAssignableFrom(rootClass) -> TripleSerializer(
+ argsSerializers[0],
+ argsSerializers[1],
+ argsSerializers[2]
+ ) as KSerializer<Any>
+
+ else -> {
+ // probably we should deprecate this method because it can't differ nullable vs non-nullable types
+ // since it uses Java TypeToken, not Kotlin one
+ val varargs = argsSerializers.map { it as KSerializer<Any?> }
+ reflectiveOrContextual(rootClass as Class<Any>, varargs)
+ }
+ }
+ }
+ is WildcardType -> serializerByJavaTypeImpl(type.upperBounds.first())
+ else -> throw IllegalArgumentException("typeToken should be an instance of Class<?>, GenericArray, ParametrizedType or WildcardType, but actual type is $type ${type::class}")
+ }
+
+@OptIn(ExperimentalSerializationApi::class)
+private fun SerializersModule.typeSerializer(type: Class<*>, failOnMissingTypeArgSerializer: Boolean): KSerializer<Any>? {
+ return if (type.isArray && !type.componentType.isPrimitive) {
+ val eType: Class<*> = type.componentType
+ val s = if (failOnMissingTypeArgSerializer) serializer(eType) else (serializerOrNull(eType) ?: return null)
+ val arraySerializer = ArraySerializer(eType.kotlin as KClass<Any>, s)
+ arraySerializer as KSerializer<Any>
+ } else {
+ reflectiveOrContextual(type as Class<Any>, emptyList())
+ }
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private fun <T : Any> SerializersModule.reflectiveOrContextual(jClass: Class<T>, typeArgumentsSerializers: List<KSerializer<Any?>>): KSerializer<T>? {
+ jClass.constructSerializerForGivenTypeArgs(*typeArgumentsSerializers.toTypedArray())?.let { return it }
+ val kClass = jClass.kotlin
+ return kClass.builtinSerializerOrNull() ?: getContextual(kClass, typeArgumentsSerializers)
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private fun SerializersModule.genericArraySerializer(
+ type: GenericArrayType,
+ failOnMissingTypeArgSerializer: Boolean
+): KSerializer<Any>? {
+ val eType = type.genericComponentType.let {
+ when (it) {
+ is WildcardType -> it.upperBounds.first()
+ else -> it
+ }
+ }
+ val serializer = if (failOnMissingTypeArgSerializer) serializer(eType) else (serializerOrNull(eType) ?: return null)
+ val kclass = when (eType) {
+ is ParameterizedType -> (eType.rawType as Class<*>).kotlin
+ is KClass<*> -> eType
+ else -> throw IllegalStateException("unsupported type in GenericArray: ${eType::class}")
+ } as KClass<Any>
+ return ArraySerializer(kclass, serializer) as KSerializer<Any>
+}
+
+private fun Type.prettyClass(): Class<*> = when (val it = this) {
+ is Class<*> -> it
+ is ParameterizedType -> it.rawType.prettyClass()
+ is WildcardType -> it.upperBounds.first().prettyClass()
+ is GenericArrayType -> it.genericComponentType.prettyClass()
+ else -> throw IllegalArgumentException("typeToken should be an instance of Class<?>, GenericArray, ParametrizedType or WildcardType, but actual type is $it ${it::class}")
+}
diff --git a/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt b/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt
new file mode 100644
index 00000000..9dbb5a06
--- /dev/null
+++ b/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import java.lang.reflect.*
+import kotlin.reflect.*
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun <T> Array<T>.getChecked(index: Int): T {
+ return get(index)
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun BooleanArray.getChecked(index: Int): Boolean {
+ return get(index)
+}
+
+@Suppress("UNCHECKED_CAST")
+internal actual fun <T : Any> KClass<T>.compiledSerializerImpl(): KSerializer<T>? =
+ this.constructSerializerForGivenTypeArgs()
+
+@Suppress("UNCHECKED_CAST")
+internal actual fun <T : Any, E : T?> ArrayList<E>.toNativeArrayImpl(eClass: KClass<T>): Array<E> =
+ toArray(java.lang.reflect.Array.newInstance(eClass.java, size) as Array<E>)
+
+internal actual fun KClass<*>.platformSpecificSerializerNotRegistered(): Nothing = serializerNotRegistered()
+
+internal fun Class<*>.serializerNotRegistered(): Nothing {
+ throw SerializationException(
+ "Serializer for class '${simpleName}' is not found.\n" +
+ "Mark the class as @Serializable or provide the serializer explicitly."
+ )
+}
+
+internal actual fun <T : Any> KClass<T>.constructSerializerForGivenTypeArgs(vararg args: KSerializer<Any?>): KSerializer<T>? {
+ return java.constructSerializerForGivenTypeArgs(*args)
+}
+
+@Suppress("UNCHECKED_CAST")
+internal fun <T: Any> Class<T>.constructSerializerForGivenTypeArgs(vararg args: KSerializer<Any?>): KSerializer<T>? {
+ if (isEnum && isNotAnnotated()) {
+ return createEnumSerializer()
+ }
+ if (isInterface) {
+ return interfaceSerializer()
+ }
+ // Search for serializer defined on companion object.
+ val serializer = invokeSerializerOnCompanion<T>(this, *args)
+ if (serializer != null) return serializer
+ // Check whether it's serializable object
+ findObjectSerializer()?.let { return it }
+ // Search for default serializer if no serializer is defined in companion object.
+ // It is required for named companions
+ val fromNamedCompanion = try {
+ declaredClasses.singleOrNull { it.simpleName == ("\$serializer") }
+ ?.getField("INSTANCE")?.get(null) as? KSerializer<T>
+ } catch (e: NoSuchFieldException) {
+ null
+ }
+ if (fromNamedCompanion != null) return fromNamedCompanion
+ // Check for polymorphic
+ return polymorphicSerializer()
+}
+
+private fun <T: Any> Class<T>.isNotAnnotated(): Boolean {
+ /*
+ * For annotated enums search serializer directly (or do not search at all?)
+ */
+ return getAnnotation(Serializable::class.java) == null &&
+ getAnnotation(Polymorphic::class.java) == null
+}
+
+private fun <T: Any> Class<T>.polymorphicSerializer(): KSerializer<T>? {
+ /*
+ * Last resort: check for @Polymorphic or Serializable(with = PolymorphicSerializer::class)
+ * annotations.
+ */
+ if (getAnnotation(Polymorphic::class.java) != null) {
+ return PolymorphicSerializer(this.kotlin)
+ }
+ val serializable = getAnnotation(Serializable::class.java)
+ if (serializable != null && serializable.with == PolymorphicSerializer::class) {
+ return PolymorphicSerializer(this.kotlin)
+ }
+ return null
+}
+
+private fun <T: Any> Class<T>.interfaceSerializer(): KSerializer<T>? {
+ /*
+ * Interfaces are @Polymorphic by default.
+ * Check if it has no annotations or `@Serializable(with = PolymorphicSerializer::class)`,
+ * otherwise bailout.
+ */
+ val serializable = getAnnotation(Serializable::class.java)
+ if (serializable == null || serializable.with == PolymorphicSerializer::class) {
+ return PolymorphicSerializer(this.kotlin)
+ }
+ return null
+}
+
+@Suppress("UNCHECKED_CAST")
+private fun <T : Any> invokeSerializerOnCompanion(jClass: Class<*>, vararg args: KSerializer<Any?>): KSerializer<T>? {
+ val companion = jClass.companionOrNull() ?: return null
+ return try {
+ val types = if (args.isEmpty()) emptyArray() else Array(args.size) { KSerializer::class.java }
+ companion.javaClass.getDeclaredMethod("serializer", *types)
+ .invoke(companion, *args) as? KSerializer<T>
+ } catch (e: NoSuchMethodException) {
+ null
+ } catch (e: InvocationTargetException) {
+ val cause = e.cause ?: throw e
+ throw InvocationTargetException(cause, cause.message ?: e.message)
+ }
+}
+
+private fun Class<*>.companionOrNull() =
+ try {
+ val companion = getDeclaredField("Companion")
+ companion.isAccessible = true
+ companion.get(null)
+ } catch (e: Throwable) {
+ null
+ }
+
+@Suppress("UNCHECKED_CAST")
+private fun <T : Any> Class<T>.createEnumSerializer(): KSerializer<T>? {
+ val constants = enumConstants
+ return EnumSerializer(canonicalName, constants as Array<out Enum<*>>) as? KSerializer<T>
+}
+
+private fun <T : Any> Class<T>.findObjectSerializer(): KSerializer<T>? {
+ // Check it is an object without using kotlin-reflect
+ val field =
+ declaredFields.singleOrNull { it.name == "INSTANCE" && it.type == this && Modifier.isStatic(it.modifiers) }
+ ?: return null
+ // Retrieve its instance and call serializer()
+ val instance = field.get(null)
+ val method =
+ methods.singleOrNull { it.name == "serializer" && it.parameterTypes.isEmpty() && it.returnType == KSerializer::class.java }
+ ?: return null
+ val result = method.invoke(instance)
+ @Suppress("UNCHECKED_CAST")
+ return result as? KSerializer<T>
+}
+
+/**
+ * Checks if an [this@isInstanceOf] is an instance of a given [kclass].
+ *
+ * This check is a replacement for [KClass.isInstance] because
+ * on JVM it requires kotlin-reflect.jar in classpath
+ * (see https://youtrack.jetbrains.com/issue/KT-14720).
+ *
+ * On JS and Native, this function delegates to aforementioned
+ * [KClass.isInstance] since it is supported there out-of-the box;
+ * on JVM, it falls back to java.lang.Class.isInstance, which causes
+ * difference when applied to function types with big arity.
+ */
+internal actual fun Any.isInstanceOf(kclass: KClass<*>): Boolean = kclass.javaObjectType.isInstance(this)
+
+internal actual fun isReferenceArray(rootClass: KClass<Any>): Boolean = rootClass.java.isArray
diff --git a/core/jvmMainModule/src/module-info.java b/core/jvmMainModule/src/module-info.java
new file mode 100644
index 00000000..1d49cd80
--- /dev/null
+++ b/core/jvmMainModule/src/module-info.java
@@ -0,0 +1,10 @@
+module kotlinx.serialization.core {
+ requires transitive kotlin.stdlib;
+
+ exports kotlinx.serialization;
+ exports kotlinx.serialization.builtins;
+ exports kotlinx.serialization.descriptors;
+ exports kotlinx.serialization.encoding;
+ exports kotlinx.serialization.internal;
+ exports kotlinx.serialization.modules;
+}
diff --git a/core/jvmTest/src/kotlinx/serialization/RetentionTest.kt b/core/jvmTest/src/kotlinx/serialization/RetentionTest.kt
new file mode 100644
index 00000000..4c46ef04
--- /dev/null
+++ b/core/jvmTest/src/kotlinx/serialization/RetentionTest.kt
@@ -0,0 +1,18 @@
+package kotlinx.serialization
+
+import org.junit.Test
+import kotlin.reflect.full.*
+import kotlin.test.*
+
+class RetentionTest {
+
+ @Serializable
+ class F(@SerialName("?") val a: Int, @Transient val b: Int = 42, @Required val c: Int)
+
+ @Test
+ fun testRetention() {
+ assertEquals("?", F::a.findAnnotation<SerialName>()?.value)
+ assertNotNull(F::b.findAnnotation<Transient>())
+ assertNotNull(F::c.findAnnotation<Required>())
+ }
+}
diff --git a/core/jvmTest/src/kotlinx/serialization/SerializationMethodInvocationOrderTest.kt b/core/jvmTest/src/kotlinx/serialization/SerializationMethodInvocationOrderTest.kt
new file mode 100644
index 00000000..31eda2fa
--- /dev/null
+++ b/core/jvmTest/src/kotlinx/serialization/SerializationMethodInvocationOrderTest.kt
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import org.junit.Test
+import kotlin.test.*
+
+class SerializationMethodInvocationOrderTest {
+
+ @Serializable
+ @SerialName("kotlinx.serialization.Container")
+ data class Container(val data: Data)
+
+ @Test
+ fun testRec() {
+ val out = Out()
+ out.encodeSerializableValue(serializer(), Container(Data("s1", 42)))
+ out.done()
+
+ val inp = Inp()
+ inp.decodeSerializableValue(serializer<Container>())
+ inp.done()
+ }
+
+ companion object {
+ fun checkContainerDesc(desc: SerialDescriptor) {
+ if (desc.serialName != "kotlinx.serialization.Container") fail("checkContainerDesc name $desc")
+ if (desc.getElementName(0) != "data") fail("checkContainerDesc $desc")
+ }
+
+ fun checkDataDesc(desc: SerialDescriptor) {
+ if (desc.serialName != "kotlinx.serialization.Data") fail("checkDataDesc name $desc")
+ if (desc.getElementName(0) != "value1") fail("checkDataDesc.0 $desc")
+ if (desc.getElementName(1) != "value2") fail("checkDataDesc.1 $desc")
+ }
+ }
+
+ class Out : AbstractEncoder() {
+ var step = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+ when (step) {
+ 1 -> {
+ checkContainerDesc(descriptor); step++; return this
+ }
+ 4 -> {
+ checkDataDesc(descriptor); step++; return this
+ }
+ }
+ fail("@$step: beginStructure($descriptor)")
+ }
+
+ override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
+ when (step) {
+ 2 -> {
+ checkContainerDesc(descriptor); if (index == 0) {
+ step++; return true
+ }
+ }
+ 5 -> {
+ checkDataDesc(descriptor); if (index == 0) {
+ step++; return true
+ }
+ }
+ 7 -> {
+ checkDataDesc(descriptor); if (index == 1) {
+ step++; return true
+ }
+ }
+ }
+ fail("@$step: encodeElement($descriptor, $index)")
+ }
+
+ override fun <T : Any?> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
+ when (step) {
+ 0, 3 -> { step++; serializer.serialize(this, value); return }
+ }
+ fail("@$step: encodeSerializableValue($value)")
+ }
+
+ override fun encodeString(value: String) {
+ when (step) {
+ 6 -> if (value == "s1") { step++; return }
+ }
+ fail("@$step: encodeString($value)")
+ }
+
+ override fun encodeInt(value: Int) {
+ when (step) {
+ 8 -> if (value == 42) { step++; return }
+ }
+ fail("@$step: decodeInt($value)")
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ when(step) {
+ 9 -> { checkDataDesc(descriptor); step++; return }
+ 10 -> { checkContainerDesc(descriptor); step++; return }
+ }
+ fail("@$step: endStructure($descriptor)")
+ }
+
+ fun done() {
+ if (step != 11) fail("@$step: OUT FAIL")
+ }
+ }
+
+ class Inp : AbstractDecoder() {
+ var step = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ when (step) {
+ 1 -> {
+ checkContainerDesc(descriptor); step++; return this
+ }
+ 4 -> {
+ checkDataDesc(descriptor); step++; return this
+ }
+ }
+ fail("@$step: beginStructure($descriptor)")
+ }
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ when (step) {
+ 2 -> {
+ checkContainerDesc(descriptor); step++; return 0
+ }
+ 5 -> {
+ checkDataDesc(descriptor); step++; return 0
+ }
+ 7 -> {
+ checkDataDesc(descriptor); step++; return 1
+ }
+ 9 -> {
+ checkDataDesc(descriptor); step++; return -1
+ }
+ 11 -> {
+ checkContainerDesc(descriptor); step++; return -1
+ }
+ }
+ fail("@$step: decodeElementIndex($descriptor)")
+ }
+
+ override fun <T : Any?> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
+ when (step) {
+ 0, 3 -> { step++; return deserializer.deserialize(this) }
+ }
+ fail("@$step: decodeSerializableValue()")
+ }
+
+ override fun decodeString(): String {
+ when (step) {
+ 6 -> { step++; return "s1" }
+ }
+ fail("@$step: decodeString()")
+ }
+
+ override fun decodeInt(): Int {
+ when (step) {
+ 8 -> { step++; return 42 }
+ }
+ fail("@$step: decodeInt()")
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ when(step) {
+ 10 -> { checkDataDesc(descriptor); step++; return }
+ 12 -> { checkContainerDesc(descriptor); step++; return }
+ }
+ fail("@$step: endStructure($descriptor)")
+ }
+
+ fun done() {
+ if (step != 13) fail("@$step: INP FAIL")
+ }
+ }
+}
diff --git a/core/jvmTest/src/kotlinx/serialization/SerializeFlatTest.kt b/core/jvmTest/src/kotlinx/serialization/SerializeFlatTest.kt
new file mode 100644
index 00000000..356e9bd6
--- /dev/null
+++ b/core/jvmTest/src/kotlinx/serialization/SerializeFlatTest.kt
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import org.junit.Test
+import kotlin.test.*
+
+// Serializable data class
+
+@Serializable
+data class Data(
+ val value1: String,
+ val value2: Int
+)
+
+// Serializable data class with explicit companion object
+
+@Serializable
+data class DataExplicit(
+ val value1: String,
+ val value2: Int
+) {
+ companion object
+}
+
+// Regular (non-data) class with var properties
+
+@Serializable
+class Reg {
+ var value1: String = ""
+ var value2: Int = 0
+}
+
+// Specify serializable names
+
+@Serializable
+data class Names(
+ @SerialName("value1")
+ val custom1: String,
+ @SerialName("value2")
+ val custom2: Int
+)
+
+// Custom serializer
+
+@Serializable(with= CustomSerializer::class)
+data class Custom(
+ val _value1: String,
+ val _value2: Int
+)
+
+@Suppress("NAME_SHADOWING")
+object CustomSerializer : KSerializer<Custom> {
+ override val descriptor = object : SerialDescriptor {
+ override val serialName = "kotlinx.serialization.Custom"
+ override val kind: SerialKind = StructureKind.CLASS
+ override val elementsCount: Int get() = 2
+ override fun getElementName(index: Int) = when(index) {
+ 0 -> "value1"
+ 1 -> "value2"
+ else -> ""
+ }
+ override fun getElementIndex(name: String) = when(name) {
+ "value1" -> 0
+ "value2" -> 1
+ else -> -1
+ }
+
+ override fun getElementAnnotations(index: Int): List<Annotation> = emptyList()
+ override fun getElementDescriptor(index: Int): SerialDescriptor = fail("Should not be called")
+ override fun isElementOptional(index: Int): Boolean = false
+ }
+
+ override fun serialize(encoder: Encoder, value: Custom) {
+ val encoder = encoder.beginStructure(descriptor)
+ encoder.encodeStringElement(descriptor, 0, value._value1)
+ encoder.encodeIntElement(descriptor, 1, value._value2)
+ encoder.endStructure(descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): Custom {
+ val decoder = decoder.beginStructure(descriptor)
+ if (decoder.decodeElementIndex(descriptor) != 0) throw java.lang.IllegalStateException()
+ val value1 = decoder.decodeStringElement(descriptor, 0)
+ if (decoder.decodeElementIndex(descriptor) != 1) throw java.lang.IllegalStateException()
+ val value2 = decoder.decodeIntElement(descriptor, 1)
+ if (decoder.decodeElementIndex(descriptor) != CompositeDecoder.DECODE_DONE) throw java.lang.IllegalStateException()
+ decoder.endStructure(descriptor)
+ return Custom(value1, value2)
+ }
+}
+
+// External serializer
+
+// not Serializable !!!
+data class ExternalData(
+ val value1: String,
+ val value2: Int
+)
+
+@Serializer(forClass= ExternalData::class)
+object ExternalSerializer
+
+// --------- tests and utils ---------
+
+class SerializeFlatTest() {
+ @Test
+ fun testData() {
+ val out = Out("Data")
+ out.encodeSerializableValue(serializer(), Data("s1", 42))
+ out.done()
+
+ val inp = Inp("Data")
+ val data = inp.decodeSerializableValue(serializer<Data>())
+ inp.done()
+ assert(data.value1 == "s1" && data.value2 == 42)
+ }
+
+ @Test
+ fun testDataExplicit() {
+ val out = Out("DataExplicit")
+ out.encodeSerializableValue(serializer(), DataExplicit("s1", 42))
+ out.done()
+
+ val inp = Inp("DataExplicit")
+ val data = inp.decodeSerializableValue(serializer<DataExplicit>())
+ inp.done()
+ assert(data.value1 == "s1" && data.value2 == 42)
+ }
+
+ @Test
+ fun testReg() {
+ val out = Out("Reg")
+ val reg = Reg()
+ reg.value1 = "s1"
+ reg.value2 = 42
+ out.encodeSerializableValue(serializer(), reg)
+ out.done()
+
+ val inp = Inp("Reg")
+ val data = inp.decodeSerializableValue(serializer<Reg>())
+ inp.done()
+ assert(data.value1 == "s1" && data.value2 == 42)
+ }
+
+ @Test
+ fun testNames() {
+ val out = Out("Names")
+ out.encodeSerializableValue(serializer(), Names("s1", 42))
+ out.done()
+
+ val inp = Inp("Names")
+ val data = inp.decodeSerializableValue(serializer<Names>())
+ inp.done()
+ assert(data.custom1 == "s1" && data.custom2 == 42)
+ }
+
+ @Test
+ fun testCustom() {
+ val out = Out("Custom")
+ out.encodeSerializableValue(CustomSerializer, Custom("s1", 42))
+ out.done()
+
+ val inp = Inp("Custom")
+ val data = inp.decodeSerializableValue(CustomSerializer)
+ inp.done()
+ assert(data._value1 == "s1" && data._value2 == 42)
+ }
+
+ @Test
+ fun testExternalData() {
+ val out = Out("ExternalData")
+ out.encodeSerializableValue(ExternalSerializer, ExternalData("s1", 42))
+ out.done()
+
+ val inp = Inp("ExternalData")
+ val data = inp.decodeSerializableValue(ExternalSerializer)
+ inp.done()
+ assert(data.value1 == "s1" && data.value2 == 42)
+ }
+
+ companion object {
+ fun fail(msg: String): Nothing = throw RuntimeException(msg)
+
+ fun checkDesc(name: String, desc: SerialDescriptor) {
+ if (desc.serialName != "kotlinx.serialization." + name) fail("checkDesc name $desc")
+ if (desc.kind != StructureKind.CLASS) fail("checkDesc kind ${desc.kind}")
+ if (desc.getElementName(0) != "value1") fail("checkDesc[0] $desc")
+ if (desc.getElementName(1) != "value2") fail("checkDesc[1] $desc")
+ }
+ }
+
+ class Out(private val name: String) : AbstractEncoder() {
+ var step = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun beginStructure(
+ descriptor: SerialDescriptor
+ ): CompositeEncoder {
+ checkDesc(name, descriptor)
+ if (step == 0) step++ else fail("@$step: beginStructure($descriptor)")
+ return this
+ }
+
+ override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
+ checkDesc(name, descriptor)
+ when (step) {
+ 1 -> if (index == 0) {
+ step++; return true
+ }
+ 3 -> if (index == 1) {
+ step++; return true
+ }
+ }
+ fail("@$step: encodeElement($descriptor, $index)")
+ }
+
+ override fun encodeString(value: String) {
+ when (step) {
+ 2 -> if (value == "s1") {
+ step++; return
+ }
+ }
+ fail("@$step: encodeString($value)")
+ }
+
+ override fun encodeInt(value: Int) {
+ when (step) {
+ 4 -> if (value == 42) { step++; return }
+ }
+ fail("@$step: decodeInt($value)")
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ checkDesc(name, descriptor)
+ if (step == 5) step++ else fail("@$step: endStructure($descriptor)")
+ }
+
+ fun done() {
+ if (step != 6) fail("@$step: OUT FAIL")
+ }
+ }
+
+ class Inp(private val name: String) : AbstractDecoder() {
+ var step = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ checkDesc(name, descriptor)
+ if (step == 0) step++ else fail("@$step: beginStructure($descriptor)")
+ return this
+ }
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ checkDesc(name, descriptor)
+ when (step) {
+ 1 -> {
+ step++; return 0
+ }
+ 3 -> {
+ step++; return 1
+ }
+ 5 -> {
+ step++; return -1
+ }
+ }
+ fail("@$step: decodeElementIndex($descriptor)")
+ }
+
+ override fun decodeString(): String {
+ when (step) {
+ 2 -> { step++; return "s1" }
+ }
+ fail("@$step: decodeString()")
+ }
+
+ override fun decodeInt(): Int {
+ when (step) {
+ 4 -> { step++; return 42 }
+ }
+ fail("@$step: decodeInt()")
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ checkDesc(name, descriptor)
+ if (step == 6) step++ else fail("@$step: endStructure($descriptor)")
+ }
+
+ fun done() {
+ if (step != 7) fail("@$step: INP FAIL")
+ }
+ }
+}
diff --git a/core/jvmTest/src/kotlinx/serialization/features/JvmContextualGenericsTest.kt b/core/jvmTest/src/kotlinx/serialization/features/JvmContextualGenericsTest.kt
new file mode 100644
index 00000000..7f0c54ca
--- /dev/null
+++ b/core/jvmTest/src/kotlinx/serialization/features/JvmContextualGenericsTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class JvmContextualGenericsTest : ContextualGenericsTest() {
+ @Test
+ fun testSurrogateSerializerFoundForGenericWithJavaType() {
+ val filledBox = ThirdPartyBox(contents = Item("Foo"))
+ val serializer = serializersModuleStatic.serializer(filledBox::class.java)
+ assertEquals(boxWithItemSerializer.descriptor, serializer.descriptor)
+ }
+
+ @Test
+ fun testSerializerFoundForContextualGenericWithJavaTypeToken() {
+ val serializerA = serializersModuleWithProvider.serializer(typeTokenOf<ThirdPartyBox<Item>>())
+ assertEquals(Item.serializer().descriptor, serializerA.descriptor.getElementDescriptor(0))
+ val serializerB = serializersModuleWithProvider.serializer(typeTokenOf<ThirdPartyBox<AnotherItem>>())
+ assertEquals(AnotherItem.serializer().descriptor, serializerB.descriptor.getElementDescriptor(0))
+ }
+}
diff --git a/core/jvmTest/src/kotlinx/serialization/features/SerializerJvmSpecificTest.kt b/core/jvmTest/src/kotlinx/serialization/features/SerializerJvmSpecificTest.kt
new file mode 100644
index 00000000..76a304b1
--- /dev/null
+++ b/core/jvmTest/src/kotlinx/serialization/features/SerializerJvmSpecificTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.internal.*
+import org.junit.Test
+import kotlin.test.*
+
+class SerializerJvmSpecificTest {
+
+ enum class Foo
+
+ @Serializable
+ abstract class ExplicitAbstract(public val i: Int = 42)
+
+ interface ImplicitInterface
+
+ @Serializable(with = PolymorphicSerializer::class)
+ interface ExplicitInterface
+
+ @Serializable
+ class Holder(
+ val iif: ImplicitInterface,
+ val eif: ExplicitInterface,
+ val ea: ExplicitAbstract
+ )
+
+
+ @Test
+ fun testNonSerializableEnum() {
+ val serializer = serializer<Foo>()
+ assertTrue(serializer.descriptor.kind is SerialKind.ENUM)
+ }
+
+ @Test
+ fun testDefaultInterfaceSerializer() {
+ assertEquals(holderChildDescriptor(0), serializer<ImplicitInterface>().descriptor)
+ }
+
+ @Test
+ fun testExplicitInterfaceSerializer() {
+ assertEquals(holderChildDescriptor(1), serializer<ExplicitInterface>().descriptor)
+ }
+
+ @Test
+ fun testDefaultAbstractSerializer() {
+ // TODO discuss on serializers equality
+ assertEquals(holderChildDescriptor(2), serializer<ExplicitAbstract>().descriptor)
+ }
+
+ private fun holderChildDescriptor(i: Int) = Holder.serializer().descriptor.getElementDescriptor(i)
+}
diff --git a/core/jvmTest/src/kotlinx/serialization/privateclasstest/PrivateDataOutOfKotlinXSerializationPackageTest.kt b/core/jvmTest/src/kotlinx/serialization/privateclasstest/PrivateDataOutOfKotlinXSerializationPackageTest.kt
new file mode 100644
index 00000000..119b9deb
--- /dev/null
+++ b/core/jvmTest/src/kotlinx/serialization/privateclasstest/PrivateDataOutOfKotlinXSerializationPackageTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.serialization.privateclasstest
+
+import kotlinx.serialization.*
+import kotlinx.serialization.SerializeFlatTest.Inp
+import kotlinx.serialization.SerializeFlatTest.Out
+import org.junit.Test
+
+// Serializable data class with private visibility that lays out of serialization library package
+
+@Serializable
+private data class DataPrivate(
+ val value1: String,
+ val value2: Int
+) {
+ companion object
+}
+
+class PrivateClassOutOfSerializationLibraryPackageTest {
+
+ @Test
+ fun testDataPrivate() {
+ val out = Out("privateclasstest.DataPrivate")
+ out.encodeSerializableValue(serializer(), DataPrivate("s1", 42))
+ out.done()
+
+ val inp = Inp("privateclasstest.DataPrivate")
+ val data = inp.decodeSerializableValue(serializer<DataPrivate>())
+ inp.done()
+ assert(data.value1 == "s1" && data.value2 == 42)
+ }
+}
diff --git a/core/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/core/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt
new file mode 100644
index 00000000..3e16ed02
--- /dev/null
+++ b/core/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt
@@ -0,0 +1,9 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+import kotlinx.serialization.test.Platform
+
+public actual val currentPlatform: Platform = Platform.JVM
diff --git a/core/jvmTest/src/kotlinx/serialization/test/TypeToken.kt b/core/jvmTest/src/kotlinx/serialization/test/TypeToken.kt
new file mode 100644
index 00000000..e4fb5be4
--- /dev/null
+++ b/core/jvmTest/src/kotlinx/serialization/test/TypeToken.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+import java.lang.reflect.*
+
+// Same classes are present in SerializerByTypeTest.kt,
+// but it seems that json-jvm-test does not depend on core-jvm-test
+
+@PublishedApi
+internal open class TypeBase<T>
+
+public inline fun <reified T> typeTokenOf(): Type {
+ val base = object : TypeBase<T>() {}
+ val superType = base::class.java.genericSuperclass!!
+ return (superType as ParameterizedType).actualTypeArguments.first()!!
+}
diff --git a/core/nativeMain/src/kotlinx/serialization/Serializers.kt b/core/nativeMain/src/kotlinx/serialization/Serializers.kt
new file mode 100644
index 00000000..9a2ffd69
--- /dev/null
+++ b/core/nativeMain/src/kotlinx/serialization/Serializers.kt
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlin.reflect.*
+
+@OptIn(ExperimentalAssociatedObjects::class)
+@AssociatedObjectKey
+@Retention(AnnotationRetention.BINARY)
+@PublishedApi
+internal annotation class SerializableWith(public val serializer: KClass<out KSerializer<*>>)
diff --git a/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt b/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt
new file mode 100644
index 00000000..e24c1820
--- /dev/null
+++ b/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.internal
+
+import kotlinx.serialization.*
+import kotlin.reflect.*
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun <T> Array<T>.getChecked(index: Int): T {
+ return get(index)
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun BooleanArray.getChecked(index: Int): Boolean {
+ return get(index)
+}
+
+internal actual fun KClass<*>.platformSpecificSerializerNotRegistered(): Nothing {
+ throw SerializationException(
+ "Serializer for class '${simpleName}' is not found.\n" +
+ "Mark the class as @Serializable or provide the serializer explicitly.\n" +
+ "On Kotlin/Native explicitly declared serializer should be used for interfaces and enums without @Serializable annotation"
+ )
+}
+
+@Suppress(
+ "UNCHECKED_CAST",
+ "DEPRECATION_ERROR"
+)
+@OptIn(ExperimentalAssociatedObjects::class)
+internal actual fun <T : Any> KClass<T>.constructSerializerForGivenTypeArgs(vararg args: KSerializer<Any?>): KSerializer<T>? =
+ when (val assocObject = findAssociatedObject<SerializableWith>()) {
+ is KSerializer<*> -> assocObject as KSerializer<T>
+ is kotlinx.serialization.internal.SerializerFactory -> assocObject.serializer(*args) as KSerializer<T>
+ else -> null
+ }
+
+@Suppress(
+ "UNCHECKED_CAST",
+ "DEPRECATION_ERROR"
+)
+@OptIn(ExperimentalAssociatedObjects::class)
+internal actual fun <T : Any> KClass<T>.compiledSerializerImpl(): KSerializer<T>? =
+ this.constructSerializerForGivenTypeArgs()
+
+internal actual fun <T : Any, E : T?> ArrayList<E>.toNativeArrayImpl(eClass: KClass<T>): Array<E> {
+ val result = arrayOfAnyNulls<E>(size)
+ var index = 0
+ for (element in this) result[index++] = element
+ @Suppress("UNCHECKED_CAST", "USELESS_CAST")
+ return result as Array<E>
+}
+
+@Suppress("UNCHECKED_CAST")
+private fun <T> arrayOfAnyNulls(size: Int): Array<T> = arrayOfNulls<Any>(size) as Array<T>
+
+internal actual fun Any.isInstanceOf(kclass: KClass<*>): Boolean = kclass.isInstance(this)
+
+internal actual fun isReferenceArray(rootClass: KClass<Any>): Boolean = rootClass == Array::class
diff --git a/core/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/core/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
new file mode 100644
index 00000000..9e51d7f4
--- /dev/null
+++ b/core/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+import kotlinx.serialization.test.Platform
+import kotlin.native.concurrent.SharedImmutable
+
+@SharedImmutable
+public actual val currentPlatform: Platform = Platform.NATIVE
diff --git a/docs/basic-serialization.md b/docs/basic-serialization.md
new file mode 100644
index 00000000..ce959f80
--- /dev/null
+++ b/docs/basic-serialization.md
@@ -0,0 +1,701 @@
+<!--- TEST_NAME BasicSerializationTest -->
+
+# Basic Serialization
+
+This is the first chapter of the [Kotlin Serialization Guide](serialization-guide.md).
+This chapter shows the basic use of Kotlin Serialization and explains its core concepts.
+
+**Table of contents**
+
+<!--- TOC -->
+
+* [Basics](#basics)
+ * [JSON encoding](#json-encoding)
+ * [JSON decoding](#json-decoding)
+* [Serializable classes](#serializable-classes)
+ * [Backing fields are serialized](#backing-fields-are-serialized)
+ * [Constructor properties requirement](#constructor-properties-requirement)
+ * [Data validation](#data-validation)
+ * [Optional properties](#optional-properties)
+ * [Optional property initializer call](#optional-property-initializer-call)
+ * [Required properties](#required-properties)
+ * [Transient properties](#transient-properties)
+ * [Defaults are not encoded by default](#defaults-are-not-encoded-by-default)
+ * [Nullable properties](#nullable-properties)
+ * [Type safety is enforced](#type-safety-is-enforced)
+ * [Referenced objects](#referenced-objects)
+ * [No compression of repeated references](#no-compression-of-repeated-references)
+ * [Generic classes](#generic-classes)
+ * [Serial field names](#serial-field-names)
+
+<!--- END -->
+
+## Basics
+
+To convert an object tree to a string or to a sequence of bytes, it must come
+through two mutually intertwined processes. In the first step, an object is _serialized_&mdash;it
+is converted into a serial sequence of its constituting primitive values. This process is common for all
+data formats and its result depends on the object being serialized. A _serializer_ controls this process.
+The second step is called _encoding_&mdash;it is the conversion of the corresponding sequence of primitives into
+the output format representation. An _encoder_ controls this process. Whenever the distinction is not important,
+both the terms of encoding and serialization are used interchangeably.
+
+```
++---------+ Serialization +------------+ Encoding +---------------+
+| Objects | --------------> | Primitives | ---------> | Output format |
++---------+ +------------+ +---------------+
+```
+
+The reverse process starts with parsing of the input format and _decoding_ of primitive values,
+followed by _deserialization_ of the resulting stream into objects. We'll see details of this process later.
+
+For now, we start with [JSON](https://json.org) encoding.
+
+<!--- INCLUDE .*-basic-.*
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+-->
+
+### JSON encoding
+
+The whole process of converting data into a specific format is called _encoding_. For JSON we encode data
+using the [Json.encodeToString][kotlinx.serialization.encodeToString] extension function. It serializes
+the object that is passed as its parameter under the hood and encodes it to a JSON string.
+
+Let's start with a class describing a project and try to get its JSON representation.
+
+```kotlin
+class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-basic-01.kt).
+
+When we run this code we get the exception.
+
+```text
+Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Project' is not found.
+Mark the class as @Serializable or provide the serializer explicitly.
+```
+
+<!--- TEST LINES_START -->
+
+Serializable classes have to be explicitly marked. Kotlin Serialization does not use reflection,
+so you cannot accidentally deserialize a class which was not supposed to be serializable. We fix it by
+adding the [`@Serializable`][Serializable] annotation.
+
+```kotlin
+@Serializable
+class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-basic-02.kt).
+
+The `@Serializable` annotation instructs the Kotlin Serialization plugin to automatically generate and hook
+up a _serializer_ for this class. Now the output of the example is the corresponding JSON.
+
+```text
+{"name":"kotlinx.serialization","language":"Kotlin"}
+```
+
+<!--- TEST -->
+
+> There is a whole chapter about the [Serializers](serializers.md). For now, it is enough to know
+> that they are automatically generated by the Kotlin Serialization plugin.
+
+### JSON decoding
+
+The reverse process is called _decoding_. To decode a JSON string into an object, we'll
+use the [Json.decodeFromString][kotlinx.serialization.decodeFromString] extension function.
+To specify which type we want to get as a result, we provide a type parameter to this function.
+
+As we'll see later, serialization works with different kinds of classes.
+Here we are marking our `Project` class as a `data class`, not because it is required, but because
+we want to print its contents to verify how it decodes.
+
+```kotlin
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":"Kotlin"}
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-basic-03.kt).
+
+Running this code we get back the object.
+
+```text
+Project(name=kotlinx.serialization, language=Kotlin)
+```
+
+<!--- TEST -->
+
+## Serializable classes
+
+This section goes into more details on how different `@Serializable` classes are handled.
+
+<!--- INCLUDE .*-classes-.*
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+-->
+
+### Backing fields are serialized
+
+Only a class's properties with backing fields are serialized, so properties with a getter/setter that don't
+have a backing field and delegated properties are not serialized, as the following example shows.
+
+```kotlin
+@Serializable
+class Project(
+ // name is a property with backing field -- serialized
+ var name: String
+) {
+ var stars: Int = 0 // property with a backing field -- serialized
+
+ val path: String // getter only, no backing field -- not serialized
+ get() = "kotlin/$name"
+
+ var id by ::name // delegated property -- not serialized
+}
+
+fun main() {
+ val data = Project("kotlinx.serialization").apply { stars = 9000 }
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-01.kt).
+
+We can clearly see that only the `name` and `stars` properties are present in the JSON output.
+
+```text
+{"name":"kotlinx.serialization","stars":9000}
+```
+
+<!--- TEST -->
+
+### Constructor properties requirement
+
+If we want to define the `Project` class so that it takes a path string, and then
+deconstructs it into the corresponding properties, we might be tempted to write something like the code below.
+
+```kotlin
+@Serializable
+class Project(path: String) {
+ val owner: String = path.substringBefore('/')
+ val name: String = path.substringAfter('/')
+}
+```
+
+<!--- CLEAR -->
+
+This class does not compile because the `@Serializable` annotation requires that all parameters of the class's primary
+constructor be properties. A simple workaround is to define a private primary constructor with the class's
+properties, and turn the constructor we wanted into the secondary one.
+
+```kotlin
+@Serializable
+class Project private constructor(val owner: String, val name: String) {
+ constructor(path: String) : this(
+ owner = path.substringBefore('/'),
+ name = path.substringAfter('/')
+ )
+
+ val path: String
+ get() = "$owner/$name"
+}
+```
+
+Serialization works with a private primary constructor, and still serializes only backing fields.
+
+```kotlin
+fun main() {
+ println(Json.encodeToString(Project("kotlin/kotlinx.serialization")))
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-02.kt).
+
+This example produces the expected output.
+
+```text
+{"owner":"kotlin","name":"kotlinx.serialization"}
+```
+
+<!--- TEST -->
+
+### Data validation
+
+Another case where you might want to introduce a primary constructor parameter without a property is when you
+want to validate its value before storing it to a property. To make it serializable you shall replace it
+with a property in the primary constructor, and move the validation to an `init { ... }` block.
+
+```kotlin
+@Serializable
+class Project(val name: String) {
+ init {
+ require(name.isNotEmpty()) { "name cannot be empty" }
+ }
+}
+```
+
+A deserialization process works like a regular constructor in Kotlin and calls all `init` blocks, ensuring that you
+cannot get an invalid class as a result of deserialization. Let's try it.
+
+```kotlin
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":""}
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-03.kt).
+
+Running this code produces the exception:
+
+```text
+Exception in thread "main" java.lang.IllegalArgumentException: name cannot be empty
+```
+
+<!--- TEST LINES_START -->
+
+### Optional properties
+
+An object can be deserialized only when all its properties are present in the input.
+For example, run the following code.
+
+```kotlin
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization"}
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-04.kt).
+
+It produces the exception:
+
+```text
+Exception in thread "main" kotlinx.serialization.MissingFieldException: Field 'language' is required for type with serial name 'example.exampleClasses04.Project', but it was missing at path: $
+```
+
+<!--- TEST LINES_START -->
+
+This problem can be fixed by adding a default value to the property, which automatically makes it optional
+for serialization.
+
+```kotlin
+@Serializable
+data class Project(val name: String, val language: String = "Kotlin")
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization"}
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-05.kt).
+
+It produces the following output with the default value for the `language` property.
+
+```text
+Project(name=kotlinx.serialization, language=Kotlin)
+```
+
+<!--- TEST -->
+
+### Optional property initializer call
+
+When an optional property is present in the input, the corresponding initializer for this
+property is not even called. This is a feature designed for performance, so be careful not
+to rely on side effects in initializers. Consider the example below.
+
+```kotlin
+fun computeLanguage(): String {
+ println("Computing")
+ return "Kotlin"
+}
+
+@Serializable
+data class Project(val name: String, val language: String = computeLanguage())
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":"Kotlin"}
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-06.kt).
+
+Since the `language` property was specified in the input, we don't see the "Computing" string printed
+in the output.
+
+```text
+Project(name=kotlinx.serialization, language=Kotlin)
+```
+
+<!--- TEST -->
+
+### Required properties
+
+A property with a default value can be required in a serial format with the [`@Required`][Required] annotation.
+Let us change the previous example by marking the `language` property as `@Required`.
+
+```kotlin
+@Serializable
+data class Project(val name: String, @Required val language: String = "Kotlin")
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization"}
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-07.kt).
+
+We get the following exception.
+
+```text
+Exception in thread "main" kotlinx.serialization.MissingFieldException: Field 'language' is required for type with serial name 'example.exampleClasses07.Project', but it was missing at path: $
+```
+
+<!--- TEST LINES_START -->
+
+### Transient properties
+
+A property can be excluded from serialization by marking it with the [`@Transient`][Transient] annotation
+(don't confuse it with [kotlin.jvm.Transient]). Transient properties must have a default value.
+
+```kotlin
+@Serializable
+data class Project(val name: String, @Transient val language: String = "Kotlin")
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":"Kotlin"}
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-08.kt).
+
+Attempts to explicitly specify its value in the serial format, even if the specified
+value is equal to the default one, produces the following exception.
+
+```text
+Exception in thread "main" kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 42: Encountered an unknown key 'language' at path: $.name
+Use 'ignoreUnknownKeys = true' in 'Json {}' builder to ignore unknown keys.
+```
+
+<!--- TEST LINES_START -->
+
+> The 'ignoreUnknownKeys' feature is explained in the [Ignoring Unknown Keys section](json.md#ignoring-unknown-keys) section.
+
+### Defaults are not encoded by default
+
+Default values are not encoded by default in JSON. This behavior is motivated by the fact that in most real-life scenarios
+such configuration reduces visual clutter, and saves the amount of data being serialized.
+
+```kotlin
+@Serializable
+data class Project(val name: String, val language: String = "Kotlin")
+
+fun main() {
+ val data = Project("kotlinx.serialization")
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-09.kt).
+
+It produces the following output, which does not have the `language` property because its value is equal to the default one.
+
+```text
+{"name":"kotlinx.serialization"}
+```
+
+<!--- TEST -->
+
+See JSON's [Encoding defaults](json.md#encoding-defaults) section on how this behavior can be configured for JSON.
+Additionally, this behavior can be controlled without taking format settings into account.
+For that purposes, [EncodeDefault] annotation can be used:
+
+```kotlin
+@Serializable
+data class Project(
+ val name: String,
+ @EncodeDefault val language: String = "Kotlin"
+)
+```
+
+This annotation instructs the framework to always serialize property, regardless of its value or format settings.
+It's also possible to tweak it into the opposite behavior using [EncodeDefault.Mode] parameter:
+
+```kotlin
+
+@Serializable
+data class User(
+ val name: String,
+ @EncodeDefault(EncodeDefault.Mode.NEVER) val projects: List<Project> = emptyList()
+)
+
+fun main() {
+ val userA = User("Alice", listOf(Project("kotlinx.serialization")))
+ val userB = User("Bob")
+ println(Json.encodeToString(userA))
+ println(Json.encodeToString(userB))
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-10.kt).
+
+As you can see, `language` property is preserved and `projects` is omitted:
+
+```text
+{"name":"Alice","projects":[{"name":"kotlinx.serialization","language":"Kotlin"}]}
+{"name":"Bob"}
+```
+
+<!--- TEST -->
+
+### Nullable properties
+
+Nullable properties are natively supported by Kotlin Serialization.
+
+```kotlin
+@Serializable
+class Project(val name: String, val renamedTo: String? = null)
+
+fun main() {
+ val data = Project("kotlinx.serialization")
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-11.kt).
+
+This example does not encode `null` in JSON because [Defaults are not encoded](#defaults-are-not-encoded).
+
+```text
+{"name":"kotlinx.serialization"}
+```
+
+<!--- TEST -->
+
+### Type safety is enforced
+
+Kotlin Serialization strongly enforces the type safety of the Kotlin programming language.
+In particular, let us try to decode a `null` value from a JSON object into a non-nullable Kotlin property `language`.
+
+```kotlin
+@Serializable
+data class Project(val name: String, val language: String = "Kotlin")
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":null}
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-12.kt).
+
+Even though the `language` property has a default value, it is still an error to attempt to assign
+the `null` value to it.
+
+```text
+Exception in thread "main" kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 52: Expected string literal but 'null' literal was found at path: $.language
+Use 'coerceInputValues = true' in 'Json {}` builder to coerce nulls to default values.
+```
+
+<!--- TEST LINES_START -->
+
+> It might be desired, when decoding 3rd-party JSONs, to coerce `null` to a default value.
+> The corresponding feature is explained in the [Coercing input values](json.md#coercing-input-values) section.
+
+### Referenced objects
+
+Serializable classes can reference other classes in their serializable properties.
+The referenced classes must be also marked as `@Serializable`.
+
+```kotlin
+@Serializable
+class Project(val name: String, val owner: User)
+
+@Serializable
+class User(val name: String)
+
+fun main() {
+ val owner = User("kotlin")
+ val data = Project("kotlinx.serialization", owner)
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-13.kt).
+
+When encoded to JSON it results in a nested JSON object.
+
+```text
+{"name":"kotlinx.serialization","owner":{"name":"kotlin"}}
+```
+
+> References to non-serializable classes can be marked as [Transient properties](#transient-properties), or a
+> custom serializer can be provided for them as shown in the [Serializers](serializers.md) chapter.
+
+<!--- TEST -->
+
+### No compression of repeated references
+
+Kotlin Serialization is designed for encoding and decoding of plain data. It does not support reconstruction
+of arbitrary object graphs with repeated object references. For example, let us try to serialize an object
+that references the same `owner` instance twice.
+
+```kotlin
+@Serializable
+class Project(val name: String, val owner: User, val maintainer: User)
+
+@Serializable
+class User(val name: String)
+
+fun main() {
+ val owner = User("kotlin")
+ val data = Project("kotlinx.serialization", owner, owner)
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-14.kt).
+
+We simply get the `owner` value encoded twice.
+
+```text
+{"name":"kotlinx.serialization","owner":{"name":"kotlin"},"maintainer":{"name":"kotlin"}}
+```
+
+> Attempt to serialize a circular structure will result in stack overflow.
+> You can use the [Transient properties](#transient-properties) to exclude some references from serialization.
+
+<!--- TEST -->
+
+### Generic classes
+
+Generic classes in Kotlin provide type-polymorphic behavior, which is enforced by Kotlin Serialization at
+compile-time. For example, consider a generic serializable class `Box<T>`.
+
+```kotlin
+@Serializable
+class Box<T>(val contents: T)
+```
+
+The `Box<T>` class can be used with builtin types like `Int`, as well as with user-defined types like `Project`.
+
+<!--- INCLUDE
+
+@Serializable
+data class Project(val name: String, val language: String)
+-->
+
+```kotlin
+@Serializable
+class Data(
+ val a: Box<Int>,
+ val b: Box<Project>
+)
+
+fun main() {
+ val data = Data(Box(42), Box(Project("kotlinx.serialization", "Kotlin")))
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-15.kt).
+
+The actual type that we get in JSON depends on the actual compile-time type parameter that was specified for `Box`.
+
+```text
+{"a":{"contents":42},"b":{"contents":{"name":"kotlinx.serialization","language":"Kotlin"}}}
+```
+
+<!--- TEST -->
+
+If the actual generic type is not serializable a compile-time error will be produced.
+
+### Serial field names
+
+The names of the properties used in encoded representation, JSON in our examples, are the same as
+their names in the source code by default. The name that is used for serialization is called a _serial name_, and
+can be changed using the [`@SerialName`][SerialName] annotation. For example, we can have a `language` property in
+the source with an abbreviated serial name.
+
+```kotlin
+@Serializable
+class Project(val name: String, @SerialName("lang") val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-classes-16.kt).
+
+Now we see that an abbreviated name `lang` is used in the JSON output.
+
+```text
+{"name":"kotlinx.serialization","lang":"Kotlin"}
+```
+
+<!--- TEST -->
+
+---
+
+The next chapter covers [Builtin classes](builtin-classes.md).
+
+<!-- stdlib references -->
+[kotlin.jvm.Transient]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-transient/
+
+<!--- MODULE /kotlinx-serialization-core -->
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
+
+[kotlinx.serialization.encodeToString]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/encode-to-string.html
+[Serializable]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html
+[kotlinx.serialization.decodeFromString]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/decode-from-string.html
+[Required]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-required/index.html
+[Transient]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-transient/index.html
+[EncodeDefault]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-encode-default/index.html
+[EncodeDefault.Mode]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-encode-default/-mode/index.html
+[SerialName]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html
+
+<!--- MODULE /kotlinx-serialization-json -->
+<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json -->
+<!--- END -->
diff --git a/docs/building.md b/docs/building.md
new file mode 100644
index 00000000..533cdcc8
--- /dev/null
+++ b/docs/building.md
@@ -0,0 +1,39 @@
+# Building Kotlin Serialization from the source
+
+## JDK version
+
+To build Kotlin Serialization JDK version 9 or higher is required.
+This is needed to compile the `module-info` file included for JPMS support.
+
+## Runtime library
+
+Kotlin Serialization runtime library itself is a [multiplatform](http://kotlinlang.org/docs/reference/multiplatform.html) project.
+To build library from the source and run all tests, use `./gradlew build`. Corresponding platform tasks like `jvmTest`, `jsTest` and so on are also available.
+
+To install it into the local Maven repository, run `./gradlew publishToMavenLocal`.
+After that, you can include this library in arbitrary projects like usual gradle dependency:
+
+```gradle
+repositories {
+ mavenLocal()
+}
+
+dependencies {
+ compile "org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version"
+}
+```
+
+To open project in Intellij IDEA, first run `./gradlew generateTestProto` from console.
+Make sure you've set an option 'Use Gradle wrapper' on import to use a correct version of Gradle.
+You may also need to mark `runtime/build/generated/source/proto/test/java` as 'Generated source root' to build project/run tests in IDE without delegating a build to Gradle.
+This requires Kotlin 1.3.11 and higher.
+
+To use snapshot version of compiler (if you have built it from sources), use flag `-Pbootstrap`. To compile and publish all Native artifacts, not only the host one, use `-Pnative.deploy=true`.
+
+`master` branch of library should be binary compatible with latest released compiler plugin. In case you want to test some new features from other branches, which are still in development and may not be compatible in terms of bytecode produced by plugin, you'll need to build the plugin by yourself.
+
+## Compiler plugin
+
+Compiler plugin for Gradle/Maven and IntelliJ plugin, starting from Kotlin 1.3, are embedded into the Kotlin compiler.
+
+Sources and steps to build it are located [here](https://github.com/JetBrains/kotlin/blob/master/plugins/kotlin-serialization/kotlin-serialization-compiler/). In general, you'll just need to run `./gradlew dist install` to get `1.x.255` versions of Kotlin compiler, stdlib and serialization plugins in the Maven local repository.
diff --git a/docs/builtin-classes.md b/docs/builtin-classes.md
new file mode 100644
index 00000000..01831154
--- /dev/null
+++ b/docs/builtin-classes.md
@@ -0,0 +1,410 @@
+<!--- TEST_NAME BuiltinClassesTest -->
+
+# Builtin classes
+
+This is the second chapter of the [Kotlin Serialization Guide](serialization-guide.md).
+In addition to all the primitive types and strings, serialization for some classes from the Kotlin standard library,
+including the standard collections, is built into Kotlin Serialization. This chapter explains the details.
+
+**Table of contents**
+
+<!--- TOC -->
+
+* [Primitives](#primitives)
+ * [Numbers](#numbers)
+ * [Long numbers](#long-numbers)
+ * [Long numbers as strings](#long-numbers-as-strings)
+ * [Enum classes](#enum-classes)
+ * [Serial names of enum entries](#serial-names-of-enum-entries)
+* [Composites](#composites)
+ * [Pair and triple](#pair-and-triple)
+ * [Lists](#lists)
+ * [Sets and other collections](#sets-and-other-collections)
+ * [Deserializing collections](#deserializing-collections)
+ * [Maps](#maps)
+ * [Unit and singleton objects](#unit-and-singleton-objects)
+
+<!--- END -->
+
+<!--- INCLUDE .*-builtin-.*
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+-->
+
+## Primitives
+
+Kotlin Serialization has the following ten primitives:
+`Boolean`, `Byte`, `Short`, `Int`, `Long`, `Float`, `Double`, `Char`, `String`, and enums.
+The other types in Kotlin Serialization are _composite_&mdash;composed of those primitive values.
+
+### Numbers
+
+All types of integer and floating-point Kotlin numbers can be serialized.
+
+<!--- INCLUDE
+import kotlin.math.*
+-->
+
+```kotlin
+@Serializable
+class Data(
+ val answer: Int,
+ val pi: Double
+)
+
+fun main() {
+ val data = Data(42, PI)
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-builtin-01.kt).
+
+Their natural representation in JSON is used.
+
+```text
+{"answer":42,"pi":3.141592653589793}
+```
+
+<!--- TEST -->
+
+> Experimental unsigned numbers as well as other experimental inline classes are not supported by Kotlin Serialization yet.
+
+
+### Long numbers
+
+Long integers are serializable, too.
+
+```kotlin
+@Serializable
+class Data(val signature: Long)
+
+fun main() {
+ val data = Data(0x1CAFE2FEED0BABE0)
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-builtin-02.kt).
+
+By default they are serialized to JSON as numbers.
+
+```text
+{"signature":2067120338512882656}
+```
+
+<!--- TEST -->
+
+### Long numbers as strings
+
+The JSON output from the previous example will get decoded normally by Kotlin Serialization running on Kotlin/JS.
+However, if we try to parse this JSON by native JavaScript methods, we get this truncated result.
+
+```
+JSON.parse("{\"signature\":2067120338512882656}")
+â–¶ {signature: 2067120338512882700}
+```
+
+The full range of a Kotlin Long does not fit in the JavaScript number, so its precision gets lost in JavaScript.
+A common workaround is to represent long numbers with full precision using the JSON string type.
+This approach is optionally supported by Kotlin Serialization with [LongAsStringSerializer], which
+can be specified for a given Long property using the [`@Serializable`][Serializable] annotation:
+
+<!--- INCLUDE
+import kotlinx.serialization.builtins.*
+-->
+
+```kotlin
+@Serializable
+class Data(
+ @Serializable(with=LongAsStringSerializer::class)
+ val signature: Long
+)
+
+fun main() {
+ val data = Data(0x1CAFE2FEED0BABE0)
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-builtin-03.kt).
+
+This JSON gets parsed natively by JavaScript without loss of precision.
+
+```text
+{"signature":"2067120338512882656"}
+```
+
+> The section on [Specifying serializers for a file](serializers.md#specifying-serializers-for-a-file) explains how a
+> serializer like `LongAsStringSerializer` can be specified for all properties in a file.
+
+<!--- TEST -->
+
+### Enum classes
+
+All enum classes are serializable out of the box without having to mark them `@Serializable`,
+as the following example shows.
+
+```kotlin
+// The @Serializable annotation is not needed for enum classes
+enum class Status { SUPPORTED }
+
+@Serializable
+class Project(val name: String, val status: Status)
+
+fun main() {
+ val data = Project("kotlinx.serialization", Status.SUPPORTED)
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-builtin-04.kt).
+
+In JSON an enum gets encoded as a string.
+
+```text
+{"name":"kotlinx.serialization","status":"SUPPORTED"}
+```
+
+<!--- TEST -->
+
+### Serial names of enum entries
+
+Serial names of enum entries can be customized with the [SerialName] annotation just like
+it was shown for properties in the [Serial field names](basic-serialization.md#serial-field-names) section.
+However, in this case, the whole enum class must be marked with the [`@Serializable`][Serializable] annotation.
+
+```kotlin
+@Serializable // required because of @SerialName
+enum class Status { @SerialName("maintained") SUPPORTED }
+
+@Serializable
+class Project(val name: String, val status: Status)
+
+fun main() {
+ val data = Project("kotlinx.serialization", Status.SUPPORTED)
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-builtin-05.kt).
+
+We see that the specified serial name is now used in the resulting JSON.
+
+```text
+{"name":"kotlinx.serialization","status":"maintained"}
+```
+
+<!--- TEST -->
+
+## Composites
+
+A number of composite types from the standard library are supported by Kotlin Serialization.
+
+### Pair and triple
+
+The simple data classes [Pair] and [Triple] from the Kotlin standard library are serializable.
+
+```kotlin
+@Serializable
+class Project(val name: String)
+
+fun main() {
+ val pair = 1 to Project("kotlinx.serialization")
+ println(Json.encodeToString(pair))
+}
+```
+
+> You can get the full code [here](../guide/example/example-builtin-06.kt).
+
+```text
+{"first":1,"second":{"name":"kotlinx.serialization"}}
+```
+
+<!--- TEST -->
+
+> Not all classes from the Kotlin standard library are serializable. In particular, ranges and the [Regex] class
+> are not serializable at the moment. Support for their serialization may be added in the future.
+
+### Lists
+
+A [List] of serializable classes can be serialized.
+
+```kotlin
+@Serializable
+class Project(val name: String)
+
+fun main() {
+ val list = listOf(
+ Project("kotlinx.serialization"),
+ Project("kotlinx.coroutines")
+ )
+ println(Json.encodeToString(list))
+}
+```
+
+> You can get the full code [here](../guide/example/example-builtin-07.kt).
+
+The result is represented as a list in JSON.
+
+```text
+[{"name":"kotlinx.serialization"},{"name":"kotlinx.coroutines"}]
+```
+
+<!--- TEST -->
+
+### Sets and other collections
+
+Other collections, like [Set], are also serializable.
+
+```kotlin
+@Serializable
+class Project(val name: String)
+
+fun main() {
+ val set = setOf(
+ Project("kotlinx.serialization"),
+ Project("kotlinx.coroutines")
+ )
+ println(Json.encodeToString(set))
+}
+```
+
+> You can get the full code [here](../guide/example/example-builtin-08.kt).
+
+[Set] is also represented as a list in JSON, like all other collections.
+
+```text
+[{"name":"kotlinx.serialization"},{"name":"kotlinx.coroutines"}]
+```
+
+<!--- TEST -->
+
+### Deserializing collections
+
+During deserialization, the type of the resulting object is determined by the static type that was specified
+in the source code&mdash;either as the type of the property or as the type parameter of the decoding function.
+The following example shows how the same JSON list of integers is deserialized into two properties of
+different Kotlin types.
+
+```kotlin
+@Serializable
+data class Data(
+ val a: List<Int>,
+ val b: Set<Int>
+)
+
+fun main() {
+ val data = Json.decodeFromString<Data>("""
+ {
+ "a": [42, 42],
+ "b": [42, 42]
+ }
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-builtin-09.kt).
+
+Because the `data.b` property is a [Set], the duplicate values from it disappeared.
+
+```text
+Data(a=[42, 42], b=[42])
+```
+
+<!--- TEST -->
+
+### Maps
+
+A [Map] with primitive or enum keys and arbitrary serializable values can be serialized.
+
+```kotlin
+@Serializable
+class Project(val name: String)
+
+fun main() {
+ val map = mapOf(
+ 1 to Project("kotlinx.serialization"),
+ 2 to Project("kotlinx.coroutines")
+ )
+ println(Json.encodeToString(map))
+}
+```
+
+> You can get the full code [here](../guide/example/example-builtin-10.kt).
+
+Kotlin maps in JSON are represented as objects. In JSON object keys are always strings, so keys are encoded as strings
+even if they are numbers in Kotlin, as we can see below.
+
+```text
+{"1":{"name":"kotlinx.serialization"},"2":{"name":"kotlinx.coroutines"}}
+```
+
+<!--- TEST -->
+
+> It is a JSON-specific limitation that keys cannot be composite.
+> It can be lifted as shown in the [Allowing structured map keys](json.md#allowing-structured-map-keys) section.
+
+
+### Unit and singleton objects
+
+The Kotlin builtin `Unit` type is also serializable.
+`Unit` is a Kotlin [singleton object](https://kotlinlang.org/docs/tutorials/kotlin-for-py/objects-and-companion-objects.html),
+and is handled equally with other Kotlin objects.
+
+Conceptually, a singleton is a class with only one instance, meaning that state does not define the object,
+but the object defines its state. In JSON, objects are serialized as empty structures.
+
+```kotlin
+@Serializable
+object SerializationVersion {
+ val libraryVersion: String = "1.0.0"
+}
+
+fun main() {
+ println(Json.encodeToString(SerializationVersion))
+ println(Json.encodeToString(Unit))
+}
+```
+
+> You can get the full code [here](../guide/example/example-builtin-11.kt).
+
+While it may seem useless at first glance, this comes in handy for sealed class serialization,
+which is explained in the [Polymorphism. Objects](polymorphism.md#objects) section.
+
+```text
+{}
+{}
+```
+
+<!--- TEST -->
+
+> Serialization of objects is format specific. Other formats may represent objects differently,
+> e.g. using their fully-qualified names.
+---
+
+The next chapter covers [Serializers](serializers.md).
+
+<!-- stdlib references -->
+[Double.NaN]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/-na-n.html
+[Pair]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/
+[Triple]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-triple/
+[Regex]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/-regex/
+[List]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/
+[Set]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-set/
+[Map]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/
+
+<!--- MODULE /kotlinx-serialization-core -->
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
+
+[Serializable]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html
+[SerialName]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html
+
+<!--- MODULE /kotlinx-serialization-core -->
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.builtins -->
+
+[LongAsStringSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.builtins/-long-as-string-serializer/index.html
+
+<!--- END -->
+
diff --git a/docs/compatibility.md b/docs/compatibility.md
new file mode 100644
index 00000000..d18336a3
--- /dev/null
+++ b/docs/compatibility.md
@@ -0,0 +1,92 @@
+# Compatibility policy
+
+This document describes the compatibility policy of kotlinx.serialization library since version 1.0.0 and Kotlin 1.4.0.
+
+Note that content of this document is applicable only for **stable** Kotlin platforms (currently Kotlin/JVM and classic Kotlin/JS),
+since other experimental platforms currently do not impose any backward-compatibility guarantees.
+You can check out what platforms are considered to be stable on [this page](https://kotlinlang.org/docs/reference/evolution/components-stability.html).
+
+- [Core library compatibility](#core-library-compatibility)
+ * [General (Stable) API](#stable-api)
+ * [Experimental API](#experimental-api)
+ * [Internal API](#internal-api)
+- [Compatibility with Kotlin compiler plugin](#compatibility-with-kotlin-compiler-plugin)
+
+## Core library compatibility
+
+Core library public API comes in three flavours: general (stable), experimental, and internal.
+All public API except stable is marked with the corresponding annotation.
+To learn how to use declarations that require opt-in, please refer to [corresponding documentation page](https://kotlinlang.org/docs/reference/opt-in-requirements.html#non-propagating-use).
+
+### Stable API
+
+Stable API is guaranteed to preserve its ABI and documented semantics:
+
+* It cannot change its semantics expressed in its documentation.
+* It is binary backwards-compatible: during update of `kotlinx.serialization` version, previously compiled code will continue to work.
+ For example, for a library that depends only on `kotlinx.serialization` stable API,
+ clients of the library can easily depend on a next `kotlinx.serialization` version and expect everything to work.
+* It is source backwards compatible modulo major deprecation. Most of the API is here to stay forever,
+unless an unfixable security or design flaw is exposed. Minor releases never add source-incompatible changes to the stable API.
+
+#### Deprecation cycle
+
+When API is deprecated, it goes through multiple stages and there is at least one major release between each stages.
+
+1. Feature is deprecated with compilation warning. Most of the time, proper replacement (and corresponding `replaceWith` declaration) is provided to automatically migrate deprecated usages with a help of IntelliJ IDEA.
+2. Deprecation level is increased to error or hidden. It is no longer possible to compile new code against deprecated API, though it is still present in the ABI.
+3. API is completely removed. While we give our best efforts not to do so and have no plans of removing any API, we still are leaving this option in case of unforeseen problems such as security issues.
+
+
+### Experimental API
+
+This API marked as `@ExperimentalSerializationApi`. API is marked experimental when its design has potential open questions which may eventually lead to either semantics changes of the API or its deprecation.
+By default, most of the new API is marked as experimental and becomes stable in one of the next releases if no new issues arise. Otherwise, either semantics is fixed without changes in ABI or API goes through deprecation cycle.
+
+However, we'll try to provide best-effort compatibility — such declarations won't be deleted or changed instantly,
+they will go through deprecation cycle if this is possible. However, this deprecation cycle may be faster than usual.
+
+Usage notes:
+
+* Experimental API can be used in your applications if maintenance cost is clear:
+additional migrations may have to be performed during `kotlinx.serialization` update.
+
+* Experimental API can be used in other **experimental** API (for example, a custom serialization format).
+In such cases, clients of the API have to be aware about experimentality.
+
+* It's not recommended to use it as a dependency in your **stable** API, even as an implementation detail.
+Due to the lack of binary backward compatibility, your clients may experience behavioural changes
+or runtime exceptions when an unexpected version of `kotlinx.serialization` gets included in the runtime classpath.
+
+### Internal API
+
+This API is marked with `@InternalSerializationApi` or located in `kotlinx.serialization.internal` package.
+It does not have any binary or source compatibility guarantees and can be deprecated or deleted without replacement at any time.
+
+It is not recommended to use it.
+However, if you have a rare use-case that can be solved only with internal API, it is possible to use it.
+In such a case, please create an issue on GitHub in order for us to understand a use-case and to provide stable alternative.
+
+## Compatibility with Kotlin compiler plugin
+
+`kotlinx.serialization` also has the compiler plugin, that generates code depending on the core library.
+Therefore, the compiler plugin should be compatible with the runtime library to work.
+Kotlin & `kotlinx.serialization` plugin 1.4.0/1.4.10 are compatible with 1.0.0 runtime library.
+
+For further updates, we have the following policy:
+
+* New Kotlin compiler plugins should be backward compatible with core library.
+It means that it is possible to freely update Kotlin version in a project without changing the code
+and without the need to update `kotlinx.serialization` runtime.
+In other words, `1.0.0` runtime can be used with any of Kotlin `1.4.x` versions.
+
+* New Kotlin compiler plugin features may require new `kotlinx.serialization` library.
+For example, if Kotlin `1.4.x` gets serialization of unsigned integers,
+it would require a corresponding runtime version higher than `1.0.0`.
+This would be indicated by a compiler error specific to a particular feature.
+
+* New core library versions may or may not require Kotlin compiler plugin update,
+depending on a particular release.
+We'll try to avoid these situations; however, in case of some unexpected issues, it may be necessary.
+So it is possible to have a situation where upgrading serialization runtime from `1.x` to `1.y` requires an update of Kotlin version from `1.4.0` to `1.4.x`.
+The compiler can detect such problems and will inform you if its version is incompatible with a current version of core library.
diff --git a/docs/formats.md b/docs/formats.md
new file mode 100644
index 00000000..790ce5f5
--- /dev/null
+++ b/docs/formats.md
@@ -0,0 +1,1420 @@
+<!--- TEST_NAME FormatsTest -->
+
+# Alternative and custom formats (experimental)
+
+This is the sixth chapter of the [Kotlin Serialization Guide](serialization-guide.md).
+It goes beyond JSON, covering alternative and custom formats. Unlike JSON, which is
+stable, these are currently experimental features of Kotlin Serialization.
+
+**Table of contents**
+
+<!--- TOC -->
+
+* [CBOR (experimental)](#cbor-experimental)
+ * [Ignoring unknown keys](#ignoring-unknown-keys)
+ * [Byte arrays and CBOR data types](#byte-arrays-and-cbor-data-types)
+* [ProtoBuf (experimental)](#protobuf-experimental)
+ * [Field numbers](#field-numbers)
+ * [Integer types](#integer-types)
+ * [Lists as repeated fields](#lists-as-repeated-fields)
+ * [Packed fields](#packed-fields)
+ * [ProtoBuf schema generator (experimental)](#protobuf-schema-generator-experimental)
+* [Properties (experimental)](#properties-experimental)
+* [Custom formats (experimental)](#custom-formats-experimental)
+ * [Basic encoder](#basic-encoder)
+ * [Basic decoder](#basic-decoder)
+ * [Sequential decoding](#sequential-decoding)
+ * [Adding collection support](#adding-collection-support)
+ * [Adding null support](#adding-null-support)
+ * [Efficient binary format](#efficient-binary-format)
+ * [Format-specific types](#format-specific-types)
+
+<!--- END -->
+
+## CBOR (experimental)
+
+[CBOR][RFC 7049] is one of the standard compact binary
+encodings for JSON, so it supports a subset of [JSON features](json.md) and
+is generally very similar to JSON in use, but produces binary data.
+
+> CBOR support is (experimentally) available in a separate
+> `org.jetbrains.kotlinx:kotlinx-serialization-cbor:<version>` module.
+
+[Cbor] class has [Cbor.encodeToByteArray] and [Cbor.decodeFromByteArray] functions.
+Let us take the basic example from the [JSON encoding](basic-serialization.md#json-encoding),
+but encode it using CBOR.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+-->
+
+```kotlin
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ val bytes = Cbor.encodeToByteArray(data)
+ println(bytes.toAsciiHexString())
+ val obj = Cbor.decodeFromByteArray<Project>(bytes)
+ println(obj)
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-01.kt).
+
+We print a filtered ASCII representation of the output, writing non-ASCII data in hex, so we see how
+all the original strings are directly represented in CBOR, but the format delimiters themselves are binary.
+
+```text
+{BF}dnameukotlinx.serializationhlanguagefKotlin{FF}
+Project(name=kotlinx.serialization, language=Kotlin)
+```
+
+<!--- TEST -->
+
+In [CBOR hex notation](http://cbor.me/), the output is equivalent to the following:
+```
+BF # map(*)
+ 64 # text(4)
+ 6E616D65 # "name"
+ 75 # text(21)
+ 6B6F746C696E782E73657269616C697A6174696F6E # "kotlinx.serialization"
+ 68 # text(8)
+ 6C616E6775616765 # "language"
+ 66 # text(6)
+ 4B6F746C696E # "Kotlin"
+ FF # primitive(*)
+```
+
+> Note, CBOR as a format, unlike JSON, supports maps with non-trivial keys
+> (see the [Allowing structured map keys](json.md#allowing-structured-map-keys) section for JSON workarounds),
+> and Kotlin maps are serialized as CBOR maps, but some parsers (like `jackson-dataformat-cbor`) don't support this.
+
+### Ignoring unknown keys
+
+CBOR format is often used to communicate with [IoT] devices where new properties could be added as a part of a device's
+API evolution. By default, unknown keys encountered during deserialization produce an error.
+This behavior can be configured with the [ignoreUnknownKeys][CborBuilder.ignoreUnknownKeys] property.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.*
+-->
+
+```kotlin
+val format = Cbor { ignoreUnknownKeys = true }
+
+@Serializable
+data class Project(val name: String)
+
+fun main() {
+ val data = format.decodeFromHexString<Project>(
+ "bf646e616d65756b6f746c696e782e73657269616c697a6174696f6e686c616e6775616765664b6f746c696eff"
+ )
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-02.kt).
+
+It decodes the object, despite the fact that `Project` is missing the `language` property.
+
+```text
+Project(name=kotlinx.serialization)
+```
+
+<!--- TEST -->
+
+In [CBOR hex notation](http://cbor.me/), the input is equivalent to the following:
+```
+BF # map(*)
+ 64 # text(4)
+ 6E616D65 # "name"
+ 75 # text(21)
+ 6B6F746C696E782E73657269616C697A6174696F6E # "kotlinx.serialization"
+ 68 # text(8)
+ 6C616E6775616765 # "language"
+ 66 # text(6)
+ 4B6F746C696E # "Kotlin"
+ FF # primitive(*)
+```
+
+### Byte arrays and CBOR data types
+
+Per the [RFC 7049 Major Types] section, CBOR supports the following data types:
+
+- Major type 0: an unsigned integer
+- Major type 1: a negative integer
+- **Major type 2: a byte string**
+- Major type 3: a text string
+- **Major type 4: an array of data items**
+- Major type 5: a map of pairs of data items
+- Major type 6: optional semantic tagging of other major types
+- Major type 7: floating-point numbers and simple data types that need no content, as well as the "break" stop code
+
+By default, Kotlin `ByteArray` instances are encoded as **major type 4**.
+When **major type 2** is desired, then the [`@ByteString`][ByteString] annotation can be used.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+-->
+
+```kotlin
+@Serializable
+data class Data(
+ @ByteString
+ val type2: ByteArray, // CBOR Major type 2
+ val type4: ByteArray // CBOR Major type 4
+)
+
+fun main() {
+ val data = Data(byteArrayOf(1, 2, 3, 4), byteArrayOf(5, 6, 7, 8))
+ val bytes = Cbor.encodeToByteArray(data)
+ println(bytes.toAsciiHexString())
+ val obj = Cbor.decodeFromByteArray<Data>(bytes)
+ println(obj)
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-03.kt).
+
+As we see, the CBOR byte that precedes the data is different for different types of encoding.
+
+```text
+{BF}etype2D{01}{02}{03}{04}etype4{9F}{05}{06}{07}{08}{FF}{FF}
+Data(type2=[1, 2, 3, 4], type4=[5, 6, 7, 8])
+```
+
+<!--- TEST -->
+
+In [CBOR hex notation](http://cbor.me/), the output is equivalent to the following:
+```
+BF # map(*)
+ 65 # text(5)
+ 7479706532 # "type2"
+ 44 # bytes(4)
+ 01020304 # "\x01\x02\x03\x04"
+ 65 # text(5)
+ 7479706534 # "type4"
+ 9F # array(*)
+ 05 # unsigned(5)
+ 06 # unsigned(6)
+ 07 # unsigned(7)
+ 08 # unsigned(8)
+ FF # primitive(*)
+ FF # primitive(*)
+```
+
+## ProtoBuf (experimental)
+
+[Protocol Buffers](https://developers.google.com/protocol-buffers) is a language-neutral binary format that normally
+relies on a separate ".proto" file that defines the protocol schema. It is more compact than CBOR, because it
+assigns integer numbers to fields instead of names.
+
+> Protocol buffers support is (experimentally) available in a separate
+> `org.jetbrains.kotlinx:kotlinx-serialization-protobuf:<version>` module.
+
+Kotlin Serialization is using proto2 semantics, where all fields are explicitly required or optional.
+For a basic example we change our example to use the
+[ProtoBuf] class with [ProtoBuf.encodeToByteArray] and [ProtoBuf.decodeFromByteArray] functions.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+-->
+
+```kotlin
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ val bytes = ProtoBuf.encodeToByteArray(data)
+ println(bytes.toAsciiHexString())
+ val obj = ProtoBuf.decodeFromByteArray<Project>(bytes)
+ println(obj)
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-04.kt).
+
+```text
+{0A}{15}kotlinx.serialization{12}{06}Kotlin
+Project(name=kotlinx.serialization, language=Kotlin)
+```
+
+<!--- TEST -->
+
+In [ProtoBuf hex notation](https://protogen.marcgravell.com/decode), the output is equivalent to the following:
+```
+Field #1: 0A String Length = 21, Hex = 15, UTF8 = "kotlinx.serialization"
+Field #2: 12 String Length = 6, Hex = 06, UTF8 = "Kotlin"
+```
+
+### Field numbers
+
+By default, field numbers in the Kotlin Serialization [ProtoBuf] implementation are automatically assigned,
+which does not provide the ability to define a stable data schema that evolves over time. That is normally achieved by
+writing a separate ".proto" file. However, with Kotlin Serialization we can get this ability without a separate
+schema file, instead using the [ProtoNumber] annotation.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+-->
+
+```kotlin
+@Serializable
+data class Project(
+ @ProtoNumber(1)
+ val name: String,
+ @ProtoNumber(3)
+ val language: String
+)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ val bytes = ProtoBuf.encodeToByteArray(data)
+ println(bytes.toAsciiHexString())
+ val obj = ProtoBuf.decodeFromByteArray<Project>(bytes)
+ println(obj)
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-05.kt).
+
+We see in the output that the number for the first property `name` did not change (as it is numbered from one by default),
+but it did change for the `language` property.
+
+```text
+{0A}{15}kotlinx.serialization{1A}{06}Kotlin
+Project(name=kotlinx.serialization, language=Kotlin)
+```
+
+<!--- TEST -->
+
+In [ProtoBuf hex notation](https://protogen.marcgravell.com/decode), the output is equivalent to the following:
+```
+Field #1: 0A String Length = 21, Hex = 15, UTF8 = "kotlinx.serialization" (total 21 chars)
+Field #3: 1A String Length = 6, Hex = 06, UTF8 = "Kotlin"
+```
+
+### Integer types
+
+Protocol buffers support various integer encodings optimized for different ranges of integers.
+They are specified using the [ProtoType] annotation and the [ProtoIntegerType] enum.
+The following example shows all three supported options.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+-->
+
+```kotlin
+@Serializable
+class Data(
+ @ProtoType(ProtoIntegerType.DEFAULT)
+ val a: Int,
+ @ProtoType(ProtoIntegerType.SIGNED)
+ val b: Int,
+ @ProtoType(ProtoIntegerType.FIXED)
+ val c: Int
+)
+
+fun main() {
+ val data = Data(1, -2, 3)
+ println(ProtoBuf.encodeToByteArray(data).toAsciiHexString())
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-06.kt).
+
+* The [default][ProtoIntegerType.DEFAULT] is a varint encoding (`intXX`) that is optimized for
+ small non-negative numbers. The value of `1` is encoded in one byte `01`.
+* The [signed][ProtoIntegerType.SIGNED] is a signed ZigZag encoding (`sintXX`) that is optimized for
+ small signed integers. The value of `-2` is encoded in one byte `03`.
+* The [fixed][ProtoIntegerType.FIXED] encoding (`fixedXX`) always uses a fixed number of bytes.
+ The value of `3` is encoded as four bytes `03 00 00 00`.
+
+> `uintXX` and `sfixedXX` protocol buffer types are not supported.
+
+```text
+{08}{01}{10}{03}{1D}{03}{00}{00}{00}
+```
+
+<!--- TEST -->
+
+In [ProtoBuf hex notation](https://protogen.marcgravell.com/decode) the output is equivalent to the following:
+```
+Field #1: 08 Varint Value = 1, Hex = 01
+Field #2: 10 Varint Value = 3, Hex = 03
+Field #3: 1D Fixed32 Value = 3, Hex = 03-00-00-00
+```
+
+### Lists as repeated fields
+
+By default, kotlin lists and other collections are representend as repeated fields.
+In the protocol buffers when the list is empty there are no elements in the
+stream with the corresponding number. For Kotlin Serialization you must explicitly specify a default of `emptyList()`
+for any property of a collection or map type. Otherwise you will not be able deserialize an empty
+list, which is indistinguishable in protocol buffers from a missing field.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+-->
+
+```kotlin
+@Serializable
+data class Data(
+ val a: List<Int> = emptyList(),
+ val b: List<Int> = emptyList()
+)
+
+fun main() {
+ val data = Data(listOf(1, 2, 3), listOf())
+ val bytes = ProtoBuf.encodeToByteArray(data)
+ println(bytes.toAsciiHexString())
+ println(ProtoBuf.decodeFromByteArray<Data>(bytes))
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-07.kt).
+
+```text
+{08}{01}{08}{02}{08}{03}
+Data(a=[1, 2, 3], b=[])
+```
+
+<!--- TEST -->
+
+In [ProtoBuf diagnostic mode](https://protogen.marcgravell.com/decode) the output is equivalent to the following:
+```
+Field #1: 08 Varint Value = 1, Hex = 01
+Field #1: 08 Varint Value = 2, Hex = 02
+Field #1: 08 Varint Value = 3, Hex = 03
+```
+
+### Packed fields
+Collection types (not maps) can be **written** as packed fields when annotated with the `@ProtoPacked` annotation.
+Per the standard packed fields can only be used on primitive numeric types. The annotation is ignored on other types.
+
+Per the [format description](https://developers.google.com/protocol-buffers/docs/encoding#packed) the parser ignores
+the annotation, but rather reads list in either packed or repeated format.
+
+### ProtoBuf schema generator (experimental)
+
+As mentioned above, when working with protocol buffers you usually use a ".proto" file and a code generator for your
+language. This includes the code to serialize your message to an output stream and deserialize it from an input stream.
+When using Kotlin Serialization this step is not necessary because your `@Serializable` Kotlin data types are used as the
+source for the schema.
+
+This is very convenient for Kotlin-to-Kotlin communication, but makes interoperability between languages complicated.
+Fortunately, you can use the ProtoBuf schema generator to output the ".proto" representation of your messages. You can
+keep your Kotlin classes as a source of truth and use traditional protoc compilers for other languages at the same time.
+
+As an example, we can display the following data class's ".proto" schema as follows.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+import kotlinx.serialization.protobuf.schema.ProtoBufSchemaGenerator
+-->
+
+```kotlin
+@Serializable
+data class SampleData(
+ val amount: Long,
+ val description: String?,
+ val department: String = "QA"
+)
+fun main() {
+ val descriptors = listOf(SampleData.serializer().descriptor)
+ val schemas = ProtoBufSchemaGenerator.generateSchemaText(descriptors)
+ println(schemas)
+}
+```
+> You can get the full code [here](../guide/example/example-formats-08.kt).
+
+Which would output as follows.
+
+```text
+syntax = "proto2";
+
+
+// serial name 'example.exampleFormats08.SampleData'
+message SampleData {
+ required int64 amount = 1;
+ optional string description = 2;
+ // WARNING: a default value decoded when value is missing
+ optional string department = 3;
+}
+
+```
+
+<!--- TEST -->
+
+Note that since default values are not represented in ".proto" files, a warning is generated when one appears in the schema.
+
+See the documentation for [ProtoBufSchemaGenerator] for more information.
+
+## Properties (experimental)
+
+Kotlin Serialization can serialize a class into a flat map with `String` keys via
+the [Properties][kotlinx.serialization.properties.Properties] format implementation.
+
+> Properties support is (experimentally) available in a separate
+> `org.jetbrains.kotlinx:kotlinx-serialization-properties:<version>` module.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.properties.Properties // todo: remove when no longer needed
+import kotlinx.serialization.properties.*
+-->
+
+```kotlin
+@Serializable
+class Project(val name: String, val owner: User)
+
+@Serializable
+class User(val name: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", User("kotlin"))
+ val map = Properties.encodeToMap(data)
+ map.forEach { (k, v) -> println("$k = $v") }
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-09.kt).
+
+The resulting map has dot-separated keys representing keys of the nested objects.
+
+```text
+name = kotlinx.serialization
+owner.name = kotlin
+```
+
+<!--- TEST -->
+
+## Custom formats (experimental)
+
+A custom format for Kotlin Serialization must provide an implementation for the [Encoder] and [Decoder] interfaces that
+we saw used in the [Serializers](serializers.md) chapter.
+These are pretty large interfaces. For convenience
+the [AbstractEncoder] and [AbstractDecoder] skeleton implementations are provided to simplify the task.
+In [AbstractEncoder] most of the `encodeXxx` methods have a default implementation that
+delegates to [`encodeValue(value: Any)`][AbstractEncoder.encodeValue] &mdash; the only method that must be
+implemented to get a basic working format.
+
+### Basic encoder
+
+Let us start with a trivial format implementation that encodes the data into a single list of primitive
+constituent objects in the order they were written in the source code. To start, we implement a simple [Encoder] by
+overriding `encodeValue` in [AbstractEncoder].
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+-->
+
+```kotlin
+class ListEncoder : AbstractEncoder() {
+ val list = mutableListOf<Any>()
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun encodeValue(value: Any) {
+ list.add(value)
+ }
+}
+```
+
+Now we write a convenience top-level function that creates an encoder that encodes an object
+and returns a list.
+
+```kotlin
+fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
+ val encoder = ListEncoder()
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.list
+}
+```
+
+For even more convenience, to avoid the need to explicitly pass a serializer, we write an `inline` overload of
+the `encodeToList` function with a `reified` type parameter using the [serializer] function to retrieve
+the appropriate [KSerializer] instance for the actual type.
+
+```kotlin
+inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
+```
+
+Now we can test it.
+
+```kotlin
+@Serializable
+data class Project(val name: String, val owner: User, val votes: Int)
+
+@Serializable
+data class User(val name: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", User("kotlin"), 9000)
+ println(encodeToList(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-10.kt).
+
+As a result, we got all the primitive values in our object graph visited and put into a list
+in _serial_ order.
+
+```text
+[kotlinx.serialization, kotlin, 9000]
+```
+
+<!--- TEST -->
+
+> By itself, that's a useful feature if we need compute some kind of hashcode or digest for all the data
+> that is contained in a serializable object tree.
+
+### Basic decoder
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+class ListEncoder : AbstractEncoder() {
+ val list = mutableListOf<Any>()
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun encodeValue(value: Any) {
+ list.add(value)
+ }
+}
+
+fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
+ val encoder = ListEncoder()
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.list
+}
+
+inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
+-->
+
+A decoder needs to implement more substance.
+
+* [decodeValue][AbstractDecoder.decodeValue] &mdash; returns the next value from the list.
+* [decodeElementIndex][CompositeDecoder.decodeElementIndex] &mdash; returns the next index of a deserialized value.
+ In this primitive format deserialization always happens in order, so we keep track of the index
+ in the `elementIndex` variable. See
+ the [Hand-written composite serializer](serializers.md#hand-written-composite-serializer) section
+ on how it ends up being used.
+* [beginStructure][Decoder.beginStructure] &mdash; returns a new instance of the `ListDecoder`, so that
+ each structure that is being recursively decoded keeps track of its own `elementIndex` state separately.
+
+```kotlin
+class ListDecoder(val list: ArrayDeque<Any>) : AbstractDecoder() {
+ private var elementIndex = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun decodeValue(): Any = list.removeFirst()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == descriptor.elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ ListDecoder(list)
+}
+```
+
+A couple of convenience functions for decoding.
+
+```kotlin
+fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
+ val decoder = ListDecoder(ArrayDeque(list))
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
+```
+
+That is enough to start encoding and decoding basic serializable classes.
+
+<!--- INCLUDE
+
+@Serializable
+data class Project(val name: String, val owner: User, val votes: Int)
+
+@Serializable
+data class User(val name: String)
+-->
+
+```kotlin
+fun main() {
+ val data = Project("kotlinx.serialization", User("kotlin"), 9000)
+ val list = encodeToList(data)
+ println(list)
+ val obj = decodeFromList<Project>(list)
+ println(obj)
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-11.kt).
+
+Now we can convert a list of primitives back to an object tree.
+
+```text
+[kotlinx.serialization, kotlin, 9000]
+Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=9000)
+```
+
+<!--- TEST -->
+
+### Sequential decoding
+
+The decoder we have implemented keeps track of the `elementIndex` in its state and implements
+`decodeElementIndex`. This means that it is going to work with an arbitrary serializer, even the
+simple one we wrote in
+the [Hand-written composite serializer](serializers.md#hand-written-composite-serializer) section.
+However, this format always stores elements in order, so this bookkeeping is not needed and
+undermines decoding performance. All auto-generated serializers on the JVM support
+the [Sequential decoding protocol (experimental)](serializers.md#sequential-decoding-protocol-experimental), and the decoder can indicate
+its support by returning `true` from the [CompositeDecoder.decodeSequentially] function.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+class ListEncoder : AbstractEncoder() {
+ val list = mutableListOf<Any>()
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun encodeValue(value: Any) {
+ list.add(value)
+ }
+}
+
+fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
+ val encoder = ListEncoder()
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.list
+}
+
+inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
+-->
+
+```kotlin
+class ListDecoder(val list: ArrayDeque<Any>) : AbstractDecoder() {
+ private var elementIndex = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun decodeValue(): Any = list.removeFirst()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == descriptor.elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ ListDecoder(list)
+
+ override fun decodeSequentially(): Boolean = true
+}
+```
+
+<!--- INCLUDE
+
+fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
+ val decoder = ListDecoder(ArrayDeque(list))
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
+
+@Serializable
+data class Project(val name: String, val owner: User, val votes: Int)
+
+@Serializable
+data class User(val name: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", User("kotlin"), 9000)
+ val list = encodeToList(data)
+ println(list)
+ val obj = decodeFromList<Project>(list)
+ println(obj)
+}
+-->
+
+> You can get the full code [here](../guide/example/example-formats-12.kt).
+
+<!--- TEST
+[kotlinx.serialization, kotlin, 9000]
+Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=9000)
+-->
+
+### Adding collection support
+
+This basic format, so far, cannot properly represent collections. In encodes them, but it does not keep
+track of how many elements there are in the collection or where it ends, so it cannot properly decode them.
+First, let us add proper support for collections to the encoder by implementing the
+[Encoder.beginCollection] function. The `beginCollection` function takes a collection size as a parameter,
+so we encode it to add it to the result.
+Our encoder implementation does not keep any state, so it just returns `this` from the `beginCollection` function.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+-->
+
+```kotlin
+class ListEncoder : AbstractEncoder() {
+ val list = mutableListOf<Any>()
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun encodeValue(value: Any) {
+ list.add(value)
+ }
+
+ override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
+ encodeInt(collectionSize)
+ return this
+ }
+}
+```
+
+<!--- INCLUDE
+
+fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
+ val encoder = ListEncoder()
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.list
+}
+
+inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
+-->
+
+The decoder, for our case, needs to only implement the [CompositeDecoder.decodeCollectionSize] function
+in addition to the previous code.
+
+> The formats that store collection size in advance have to return `true` from `decodeSequentially`.
+
+```kotlin
+class ListDecoder(val list: ArrayDeque<Any>, var elementsCount: Int = 0) : AbstractDecoder() {
+ private var elementIndex = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun decodeValue(): Any = list.removeFirst()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ ListDecoder(list, descriptor.elementsCount)
+
+ override fun decodeSequentially(): Boolean = true
+
+ override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
+ decodeInt().also { elementsCount = it }
+}
+```
+
+<!--- INCLUDE
+
+fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
+ val decoder = ListDecoder(ArrayDeque(list))
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
+-->
+
+That is all that is needed to support collections and maps.
+
+```kotlin
+@Serializable
+data class Project(val name: String, val owners: List<User>, val votes: Int)
+
+@Serializable
+data class User(val name: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", listOf(User("kotlin"), User("jetbrains")), 9000)
+ val list = encodeToList(data)
+ println(list)
+ val obj = decodeFromList<Project>(list)
+ println(obj)
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-13.kt).
+
+We see the size of the list added to the result, letting the decoder know where to stop.
+
+```text
+[kotlinx.serialization, 2, kotlin, jetbrains, 9000]
+Project(name=kotlinx.serialization, owners=[User(name=kotlin), User(name=jetbrains)], votes=9000)
+```
+
+<!--- TEST -->
+
+### Adding null support
+
+Our trivial format does not support `null` values so far. For nullable types we need to add some kind
+of "null indicator", telling whether the upcoming value is null or not.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+class ListEncoder : AbstractEncoder() {
+ val list = mutableListOf<Any>()
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun encodeValue(value: Any) {
+ list.add(value)
+ }
+
+ override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
+ encodeInt(collectionSize)
+ return this
+ }
+-->
+
+In the encoder implementation we override [Encoder.encodeNull] and [Encoder.encodeNotNullMark].
+
+```kotlin
+ override fun encodeNull() = encodeValue("NULL")
+ override fun encodeNotNullMark() = encodeValue("!!")
+```
+
+<!--- INCLUDE
+}
+
+fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
+ val encoder = ListEncoder()
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.list
+}
+
+inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
+
+class ListDecoder(val list: ArrayDeque<Any>, var elementsCount: Int = 0) : AbstractDecoder() {
+ private var elementIndex = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun decodeValue(): Any = list.removeFirst()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ ListDecoder(list, descriptor.elementsCount)
+
+ override fun decodeSequentially(): Boolean = true
+
+ override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
+ decodeInt().also { elementsCount = it }
+-->
+
+In the decoder implementation we override [Decoder.decodeNotNullMark].
+
+```kotlin
+ override fun decodeNotNullMark(): Boolean = decodeString() != "NULL"
+```
+
+<!--- INCLUDE
+}
+
+fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
+ val decoder = ListDecoder(ArrayDeque(list))
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
+-->
+
+Let us test nullable properties both with not-null and null values.
+
+```kotlin
+@Serializable
+data class Project(val name: String, val owner: User?, val votes: Int?)
+
+@Serializable
+data class User(val name: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", User("kotlin") , null)
+ val list = encodeToList(data)
+ println(list)
+ val obj = decodeFromList<Project>(list)
+ println(obj)
+}
+
+```
+
+> You can get the full code [here](../guide/example/example-formats-14.kt).
+
+In the output we see how not-null`!!` and `NULL` marks are used.
+
+```text
+[kotlinx.serialization, !!, kotlin, NULL]
+Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=null)
+```
+
+<!--- TEST -->
+
+### Efficient binary format
+
+Now we are ready for an example of an efficient binary format. We are going to write data to the
+[java.io.DataOutput] implementation. Instead of `encodeValue` we must override the individual
+`encodeXxx` functions for each of ten [primitives](builtin-classes.md#primitives) in the encoder.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import java.io.*
+-->
+
+```kotlin
+class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() {
+ override val serializersModule: SerializersModule = EmptySerializersModule
+ override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
+ override fun encodeByte(value: Byte) = output.writeByte(value.toInt())
+ override fun encodeShort(value: Short) = output.writeShort(value.toInt())
+ override fun encodeInt(value: Int) = output.writeInt(value)
+ override fun encodeLong(value: Long) = output.writeLong(value)
+ override fun encodeFloat(value: Float) = output.writeFloat(value)
+ override fun encodeDouble(value: Double) = output.writeDouble(value)
+ override fun encodeChar(value: Char) = output.writeChar(value.code)
+ override fun encodeString(value: String) = output.writeUTF(value)
+ override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = output.writeInt(index)
+
+ override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
+ encodeInt(collectionSize)
+ return this
+ }
+
+ override fun encodeNull() = encodeBoolean(false)
+ override fun encodeNotNullMark() = encodeBoolean(true)
+}
+```
+
+<!--- INCLUDE
+
+fun <T> encodeTo(output: DataOutput, serializer: SerializationStrategy<T>, value: T) {
+ val encoder = DataOutputEncoder(output)
+ encoder.encodeSerializableValue(serializer, value)
+}
+
+inline fun <reified T> encodeTo(output: DataOutput, value: T) = encodeTo(output, serializer(), value)
+-->
+
+The decoder implementation mirrors encoder's implementation overriding all the primitive `decodeXxx` functions.
+
+```kotlin
+class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() {
+ private var elementIndex = 0
+ override val serializersModule: SerializersModule = EmptySerializersModule
+ override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
+ override fun decodeByte(): Byte = input.readByte()
+ override fun decodeShort(): Short = input.readShort()
+ override fun decodeInt(): Int = input.readInt()
+ override fun decodeLong(): Long = input.readLong()
+ override fun decodeFloat(): Float = input.readFloat()
+ override fun decodeDouble(): Double = input.readDouble()
+ override fun decodeChar(): Char = input.readChar()
+ override fun decodeString(): String = input.readUTF()
+ override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = input.readInt()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ DataInputDecoder(input, descriptor.elementsCount)
+
+ override fun decodeSequentially(): Boolean = true
+
+ override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
+ decodeInt().also { elementsCount = it }
+
+ override fun decodeNotNullMark(): Boolean = decodeBoolean()
+}
+```
+
+<!--- INCLUDE
+
+fun <T> decodeFrom(input: DataInput, deserializer: DeserializationStrategy<T>): T {
+ val decoder = DataInputDecoder(input)
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFrom(input: DataInput): T = decodeFrom(input, serializer())
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+-->
+
+We can now serialize and deserialize arbitrary data. For example, the same classes as were
+used in the [CBOR (experimental)](#cbor-experimental) and [ProtoBuf (experimental)](#protobuf-experimental) sections.
+
+```kotlin
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ val output = ByteArrayOutputStream()
+ encodeTo(DataOutputStream(output), data)
+ val bytes = output.toByteArray()
+ println(bytes.toAsciiHexString())
+ val input = ByteArrayInputStream(bytes)
+ val obj = decodeFrom<Project>(DataInputStream(input))
+ println(obj)
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-15.kt).
+
+As we can see, the result is a dense binary format that only contains the data that is being serialized.
+It can be easily tweaked for any kind of domain-specific compact encoding.
+
+```text
+{00}{15}kotlinx.serialization{00}{06}Kotlin
+Project(name=kotlinx.serialization, language=Kotlin)
+```
+
+<!--- TEST -->
+
+### Format-specific types
+
+A format implementation might provide special support for data types that are not among the list of primitive
+types in Kotlin Serialization, and do not have a corresponding `encodeXxx`/`decodeXxx` function.
+In the encoder this is achieved by overriding the
+[`encodeSerializableValue(serializer, value)`][Encoder.encodeSerializableValue] function.
+
+In our `DataOutput` format example we might want to provide a specialized efficient data path for serializing an array
+of bytes since [DataOutput][java.io.DataOutput] has a special method for this purpose.
+
+Detection of the type is performed by looking at the `serializer.descriptor`, not by checking the type of the `value`
+being serialized, so we fetch the builtin [KSerializer] instance for `ByteArray` type.
+
+> This an important difference. This way our format implementation properly supports
+> [Custom serializers](serializers.md#custom-serializers) that a user might specify for a type that just happens
+> to be internally represented as a byte array, but need a different serial representation.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.encoding.*
+import java.io.*
+-->
+
+```kotlin
+private val byteArraySerializer = serializer<ByteArray>()
+```
+
+> Specifically for byte arrays, we could have also used the builtin
+> [ByteArraySerializer][kotlinx.serialization.builtins.ByteArraySerializer()] function.
+
+We add the corresponding code to the [Encoder] implementation of our
+[Efficient binary format](#efficient-binary-format). To make our `ByteArray` encoding even more efficient,
+we add a trivial implementation of `encodeCompactSize` function that uses only one byte to represent
+a size of up to 254 bytes.
+
+<!--- INCLUDE
+class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() {
+ override val serializersModule: SerializersModule = EmptySerializersModule
+ override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
+ override fun encodeByte(value: Byte) = output.writeByte(value.toInt())
+ override fun encodeShort(value: Short) = output.writeShort(value.toInt())
+ override fun encodeInt(value: Int) = output.writeInt(value)
+ override fun encodeLong(value: Long) = output.writeLong(value)
+ override fun encodeFloat(value: Float) = output.writeFloat(value)
+ override fun encodeDouble(value: Double) = output.writeDouble(value)
+ override fun encodeChar(value: Char) = output.writeChar(value.code)
+ override fun encodeString(value: String) = output.writeUTF(value)
+ override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = output.writeInt(index)
+
+ override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
+ encodeInt(collectionSize)
+ return this
+ }
+
+ override fun encodeNull() = encodeBoolean(false)
+ override fun encodeNotNullMark() = encodeBoolean(true)
+-->
+
+```kotlin
+ override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
+ if (serializer.descriptor == byteArraySerializer.descriptor)
+ encodeByteArray(value as ByteArray)
+ else
+ super.encodeSerializableValue(serializer, value)
+ }
+
+ private fun encodeByteArray(bytes: ByteArray) {
+ encodeCompactSize(bytes.size)
+ output.write(bytes)
+ }
+
+ private fun encodeCompactSize(value: Int) {
+ if (value < 0xff) {
+ output.writeByte(value)
+ } else {
+ output.writeByte(0xff)
+ output.writeInt(value)
+ }
+ }
+```
+
+<!--- INCLUDE
+}
+
+fun <T> encodeTo(output: DataOutput, serializer: SerializationStrategy<T>, value: T) {
+ val encoder = DataOutputEncoder(output)
+ encoder.encodeSerializableValue(serializer, value)
+}
+
+inline fun <reified T> encodeTo(output: DataOutput, value: T) = encodeTo(output, serializer(), value)
+
+class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() {
+ private var elementIndex = 0
+ override val serializersModule: SerializersModule = EmptySerializersModule
+ override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
+ override fun decodeByte(): Byte = input.readByte()
+ override fun decodeShort(): Short = input.readShort()
+ override fun decodeInt(): Int = input.readInt()
+ override fun decodeLong(): Long = input.readLong()
+ override fun decodeFloat(): Float = input.readFloat()
+ override fun decodeDouble(): Double = input.readDouble()
+ override fun decodeChar(): Char = input.readChar()
+ override fun decodeString(): String = input.readUTF()
+ override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = input.readInt()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ DataInputDecoder(input, descriptor.elementsCount)
+
+ override fun decodeSequentially(): Boolean = true
+
+ override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
+ decodeInt().also { elementsCount = it }
+
+ override fun decodeNotNullMark(): Boolean = decodeBoolean()
+-->
+
+A similar code is added to the [Decoder] implementation. Here we override
+the [decodeSerializableValue][Decoder.decodeSerializableValue] function.
+
+```kotlin
+ @Suppress("UNCHECKED_CAST")
+ override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>, previousValue: T?): T =
+ if (deserializer.descriptor == byteArraySerializer.descriptor)
+ decodeByteArray() as T
+ else
+ super.decodeSerializableValue(deserializer, previousValue)
+
+ private fun decodeByteArray(): ByteArray {
+ val bytes = ByteArray(decodeCompactSize())
+ input.readFully(bytes)
+ return bytes
+ }
+
+ private fun decodeCompactSize(): Int {
+ val byte = input.readByte().toInt() and 0xff
+ if (byte < 0xff) return byte
+ return input.readInt()
+ }
+```
+
+<!--- INCLUDE
+}
+
+fun <T> decodeFrom(input: DataInput, deserializer: DeserializationStrategy<T>): T {
+ val decoder = DataInputDecoder(input)
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFrom(input: DataInput): T = decodeFrom(input, serializer())
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+-->
+
+Now everything is ready to perform serialization of some byte arrays.
+
+```kotlin
+@Serializable
+data class Project(val name: String, val attachment: ByteArray)
+
+fun main() {
+ val data = Project("kotlinx.serialization", byteArrayOf(0x0A, 0x0B, 0x0C, 0x0D))
+ val output = ByteArrayOutputStream()
+ encodeTo(DataOutputStream(output), data)
+ val bytes = output.toByteArray()
+ println(bytes.toAsciiHexString())
+ val input = ByteArrayInputStream(bytes)
+ val obj = decodeFrom<Project>(DataInputStream(input))
+ println(obj)
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-16.kt).
+
+As we can see, our custom byte array format is being used, with the compact encoding of its size in one byte.
+
+```text
+{00}{15}kotlinx.serialization{04}{0A}{0B}{0C}{0D}
+Project(name=kotlinx.serialization, attachment=[10, 11, 12, 13])
+```
+
+<!--- TEST -->
+
+---
+
+This chapter concludes [Kotlin Serialization Guide](serialization-guide.md).
+
+
+<!-- references -->
+[RFC 7049]: https://tools.ietf.org/html/rfc7049
+[IoT]: https://en.wikipedia.org/wiki/Internet_of_things
+[RFC 7049 Major Types]: https://tools.ietf.org/html/rfc7049#section-2.1
+
+<!-- Java references -->
+[java.io.DataOutput]: https://docs.oracle.com/javase/8/docs/api/java/io/DataOutput.html
+
+<!--- MODULE /kotlinx-serialization-core -->
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
+
+[serializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/serializer.html
+[KSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.builtins -->
+
+[kotlinx.serialization.builtins.ByteArraySerializer()]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.builtins/-byte-array-serializer.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding -->
+
+[Encoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html
+[Decoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html
+[AbstractEncoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-encoder/index.html
+[AbstractDecoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-decoder/index.html
+[AbstractEncoder.encodeValue]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-encoder/encode-value.html
+[AbstractDecoder.decodeValue]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-decoder/decode-value.html
+[CompositeDecoder.decodeElementIndex]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-element-index.html
+[Decoder.beginStructure]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/begin-structure.html
+[CompositeDecoder.decodeSequentially]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-sequentially.html
+[Encoder.beginCollection]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/begin-collection.html
+[CompositeDecoder.decodeCollectionSize]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-collection-size.html
+[Encoder.encodeNull]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-null.html
+[Encoder.encodeNotNullMark]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-not-null-mark.html
+[Decoder.decodeNotNullMark]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/decode-not-null-mark.html
+[Encoder.encodeSerializableValue]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-serializable-value.html
+[Decoder.decodeSerializableValue]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/decode-serializable-value.html
+
+<!--- MODULE /kotlinx-serialization-properties -->
+<!--- INDEX kotlinx-serialization-properties/kotlinx.serialization.properties -->
+
+[kotlinx.serialization.properties.Properties]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-properties/kotlinx.serialization.properties/-properties/index.html
+
+<!--- MODULE /kotlinx-serialization-protobuf -->
+<!--- INDEX kotlinx-serialization-protobuf/kotlinx.serialization.protobuf -->
+
+[ProtoBuf]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-buf/index.html
+[ProtoBuf.encodeToByteArray]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-buf/encode-to-byte-array.html
+[ProtoBuf.decodeFromByteArray]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-buf/decode-from-byte-array.html
+[ProtoNumber]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-number/index.html
+[ProtoType]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-type/index.html
+[ProtoIntegerType]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/index.html
+[ProtoIntegerType.DEFAULT]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/-d-e-f-a-u-l-t/index.html
+[ProtoIntegerType.SIGNED]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/-s-i-g-n-e-d/index.html
+[ProtoIntegerType.FIXED]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/-f-i-x-e-d/index.html
+
+<!--- INDEX kotlinx-serialization-protobuf/kotlinx.serialization.protobuf.schema -->
+
+[ProtoBufSchemaGenerator]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf.schema/-proto-buf-schema-generator/index.html
+
+<!--- MODULE /kotlinx-serialization-cbor -->
+<!--- INDEX kotlinx-serialization-cbor/kotlinx.serialization.cbor -->
+
+[Cbor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor/index.html
+[Cbor.encodeToByteArray]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor/encode-to-byte-array.html
+[Cbor.decodeFromByteArray]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor/decode-from-byte-array.html
+[CborBuilder.ignoreUnknownKeys]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor-builder/ignore-unknown-keys.html
+[ByteString]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-byte-string/index.html
+
+<!--- END -->
diff --git a/docs/inline-classes.md b/docs/inline-classes.md
new file mode 100644
index 00000000..a28f51c0
--- /dev/null
+++ b/docs/inline-classes.md
@@ -0,0 +1,205 @@
+# Serialization and inline classes (experimental, IR-specific)
+
+This appendix describes how inline classes are handled by kotlinx.serialization.
+
+> Features described in this document are currently [experimental](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/compatibility.md#experimental-api)
+> and are available only with IR compilers. Native targets use IR compiler by default;
+> see documentation for [JS](https://kotlinlang.org/docs/reference/js-ir-compiler.html) and [JVM](https://kotlinlang.org/docs/reference/whatsnew14.html#new-jvm-ir-backend) to learn how to enable IR compilers.
+> Inline classes themselves are an [Alpha](https://kotlinlang.org/docs/reference/inline-classes.html#alpha-status-of-inline-classes) Kotlin feature.
+
+**Table of contents**
+
+<!--- TOC -->
+
+* [Serializable inline classes](#serializable-inline-classes)
+* [Unsigned types support (JSON only)](#unsigned-types-support-json-only)
+* [Using inline classes in your custom serializers](#using-inline-classes-in-your-custom-serializers)
+
+<!--- END -->
+
+## Serializable inline classes
+
+We can mark inline class as serializable:
+
+```kotlin
+@Serializable
+inline class Color(val rgb: Int)
+```
+
+Inline class in Kotlin is stored as its underlying type when possible (i.e. no boxing is required).
+Serialization framework makes does not impose any additional restriction and uses the underlying type where possible as well.
+
+```kotlin
+@Serializable
+data class NamedColor(val color: Color, val name: String)
+
+fun main() {
+ println(Json.encodeToString(NamedColor(Color(0), "black")))
+}
+```
+
+In this example, `NamedColor` is serialized as two primitives: `color: Int` and `name: String` without an allocation
+of `Color` class. When we run the example, encoding data with JSON format, we get the following
+output:
+
+```text
+{"color": 0, "name": "black"}
+```
+
+As we see, `Color` class is not included during the encoding, only its underlying data. This invariant holds even if the actual inline class
+is [allocated](https://kotlinlang.org/docs/reference/inline-classes.html#representation) — for example, when inline
+class is used as a generic type argument:
+
+```kotlin
+@Serializable
+class Palette(val colors: List<Color>)
+
+fun main() {
+ println(Json.encodeToString(Palette(listOf(Color(0), Color(255), Color(128)))))
+}
+```
+
+The snippet produces the following output:
+
+```text
+{"colors":[0, 255, 128]}
+```
+
+## Unsigned types support (JSON only)
+
+Kotlin standard library provides ready-to-use unsigned arithmetics, leveraging inline classes
+to represent unsigned types: `UByte`, `UShort`, `UInt` and `ULong`.
+[Json] format has built-in support for them: these types are serialized as theirs string
+representations in unsigned form.
+These types are handled as regular serializable types by the compiler plugin and can be freely used in serializable classes:
+
+```kotlin
+@Serializable
+class Counter(val counted: UByte, val description: String)
+
+fun main() {
+ val counted = 239.toUByte()
+ println(Json.encodeToString(Counter(counted, "tries")))
+}
+```
+
+The output is following:
+
+```text
+{"counted":239,"description":"tries"}
+```
+
+> Unsigned types are currently unsupported in Protobuf and CBOR, but we plan to add them later.
+
+## Using inline classes in your custom serializers
+
+Let's return to our `NamedColor` example and try to write a custom serializer for it. Normally, as shown
+in [Hand-written composite serializer](serializers.md#hand-written-composite-serializer), we would write the following code
+in `serialize` method:
+
+```kotlin
+override fun serialize(encoder: Encoder, value: NamedColor) {
+ encoder.beginStructure(descriptor) {
+ encodeSerializableElement(descriptor, 0, Color.serializer(), value.color)
+ encodeStringElement(descriptor, 1, value.name)
+ }
+}
+```
+
+However, since `Color` is used as a type argument in [encodeSerializableElement][CompositeEncoder.encodeSerializableElement] function, `value.color` will be boxed
+to `Color` wrapper before passing it to the function, preventing the inline class optimization. To avoid this, we can use
+special [encodeInlineElement][CompositeEncoder.encodeInlineElement] function instead. It uses [serial descriptor][SerialDescriptor] of `Color` ([retrieved][SerialDescriptor.getElementDescriptor] from serial descriptor of `NamedColor`) instead of [KSerializer],
+does not have type parameters and does not accept any values. Instead, it returns [Encoder]. Using it, we can encode
+unboxed value:
+
+```kotlin
+override fun serialize(encoder: Encoder, value: NamedColor) {
+ encoder.beginStructure(descriptor) {
+ encodeInlineElement(descriptor, 0).encodeInt(value.color)
+ encodeStringElement(descriptor, 1, value.name)
+ }
+}
+```
+
+The same principle goes also with [CompositeDecoder]: it has [decodeInlineElement][CompositeDecoder.decodeInlineElement] function that returns [Decoder].
+
+If your class should be represented as a primitive (as shown in [Primitive serializer](serializers.md#primitive-serializer) section),
+and you cannot use [beginStructure][Encoder.beginStructure] function, there is a complementary function in [Encoder] called [encodeInline][Encoder.encodeInline].
+We will use it to show an example how one can represent a class as an unsigned integer.
+
+Let's start with a UID class:
+
+```kotlin
+@Serializable(UIDSerializer::class)
+class UID(val uid: Int)
+```
+
+`uid` type is `Int`, but suppose we want it to be an unsigned integer in JSON. We can start writing the
+following custom serializer:
+
+```kotlin
+object UIDSerializer: KSerializer<UID> {
+ override val descriptor = UInt.serializer().descriptor
+}
+```
+
+Note that we are using here descriptor from `UInt.serializer()` — it means that the class' representation looks like a
+UInt's one.
+
+Then the `serialize` method:
+
+```kotlin
+override fun serialize(encoder: Encoder, value: UID) {
+ encoder.encodeInline(descriptor).encodeInt(value.uid)
+}
+```
+
+That's where the magic happens — despite we called a regular [encodeInt][Encoder.encodeInt] with a `uid: Int` argument, the output will contain
+an unsigned int because of the special encoder from `encodeInline` function. Since JSON format supports unsigned integers, it
+recognizes theirs descriptors when they're passed into `encodeInline` and handles consecutive calls as for unsigned integers.
+
+The `deserialize` method looks symmetrically:
+
+```kotlin
+override fun deserialize(decoder: Decoder): UID {
+ return UID(decoder.decodeInline(descriptor).decodeInt())
+}
+```
+
+> Disclaimer: You can also write such a serializer for inline class itself (imagine UID being the inline class — there's no need to change anything in the serializer).
+> However, do not use anything in custom serializers for inline classes besides `encodeInline`. As we discussed, calls to inline class serializer may be
+> optimized and replaced with a `encodeInlineElement` calls.
+> `encodeInline` and `encodeInlineElement` calls with the same descriptor are considered equivalent and can be replaced with each other — formats should return the same `Encoder`.
+> If you embed custom logic in custom inline class serializer, you may get different results depending on whether this serializer was called at all
+> (and this, in turn, depends on whether inline class was boxed or not).
+
+---
+
+<!--- MODULE /kotlinx-serialization-core -->
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
+
+[KSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding -->
+
+[CompositeEncoder.encodeSerializableElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-encoder/encode-serializable-element.html
+[CompositeEncoder.encodeInlineElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-encoder/encode-inline-element.html
+[Encoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html
+[CompositeDecoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/index.html
+[CompositeDecoder.decodeInlineElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-inline-element.html
+[Decoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html
+[Encoder.beginStructure]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/begin-structure.html
+[Encoder.encodeInline]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-inline.html
+[Encoder.encodeInt]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-int.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.descriptors -->
+
+[SerialDescriptor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-descriptor/index.html
+[SerialDescriptor.getElementDescriptor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-descriptor/get-element-descriptor.html
+
+<!--- MODULE /kotlinx-serialization-json -->
+<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json -->
+
+[Json]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html
+
+<!--- END -->
diff --git a/docs/json.md b/docs/json.md
new file mode 100644
index 00000000..606d4aca
--- /dev/null
+++ b/docs/json.md
@@ -0,0 +1,1093 @@
+<!--- TEST_NAME JsonTest -->
+
+# JSON features
+
+This is the fifth chapter of the [Kotlin Serialization Guide](serialization-guide.md).
+In this chapter, we'll walk through features of [JSON](https://www.json.org/json-en.html) serialization available in the [Json] class.
+
+**Table of contents**
+
+<!--- TOC -->
+
+* [Json configuration](#json-configuration)
+ * [Pretty printing](#pretty-printing)
+ * [Lenient parsing](#lenient-parsing)
+ * [Ignoring unknown keys](#ignoring-unknown-keys)
+ * [Alternative Json names](#alternative-json-names)
+ * [Coercing input values](#coercing-input-values)
+ * [Encoding defaults](#encoding-defaults)
+ * [Explicit nulls](#explicit-nulls)
+ * [Allowing structured map keys](#allowing-structured-map-keys)
+ * [Allowing special floating-point values](#allowing-special-floating-point-values)
+ * [Class discriminator for polymorphism](#class-discriminator-for-polymorphism)
+* [Json elements](#json-elements)
+ * [Parsing to Json element](#parsing-to-json-element)
+ * [Types of Json elements](#types-of-json-elements)
+ * [Json element builders](#json-element-builders)
+ * [Decoding Json elements](#decoding-json-elements)
+* [Json transformations](#json-transformations)
+ * [Array wrapping](#array-wrapping)
+ * [Array unwrapping](#array-unwrapping)
+ * [Manipulating default values](#manipulating-default-values)
+ * [Content-based polymorphic deserialization](#content-based-polymorphic-deserialization)
+ * [Under the hood (experimental)](#under-the-hood-experimental)
+ * [Maintaining custom JSON attributes](#maintaining-custom-json-attributes)
+
+<!--- END -->
+
+## Json configuration
+
+The default [Json] implementation is quite strict with respect to invalid inputs. It enforces Kotlin type safety and
+restricts Kotlin values that can be serialized so that the resulting JSON representations are standard.
+Many non-standard JSON features are supported by creating a custom instance of a JSON _format_.
+
+To use a custom JSON format configuration, create your own [Json] class instance from an existing
+instance, such as a default `Json` object, using the [Json()] builder function. Specify parameter values
+in the parentheses via the [JsonBuilder] DSL. The resulting `Json` format instance is immutable and thread-safe;
+it can be simply stored in a top-level property.
+
+> We recommend that you store and reuse custom instances of formats for performance reasons because format implementations
+> may cache format-specific additional information about the classes they serialize.
+
+This chapter shows configuration features that [Json] supports.
+
+<!--- INCLUDE .*-json-.*
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+-->
+
+### Pretty printing
+
+By default, the [Json] output is a single line. You can configure it to pretty print the output (that is, add indentations
+and line breaks for better readability) by setting the [prettyPrint][JsonBuilder.prettyPrint] property to `true`:
+
+```kotlin
+val format = Json { prettyPrint = true }
+
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-01.kt).
+
+It gives the following nice result:
+
+```text
+{
+ "name": "kotlinx.serialization",
+ "language": "Kotlin"
+}
+```
+
+<!--- TEST -->
+
+### Lenient parsing
+
+By default, [Json] parser enforces various JSON restrictions to be as specification-compliant as possible
+(see [RFC-4627]). Particularly, keys must be quoted, while literals must be unquoted. Those restrictions can be relaxed with
+the [isLenient][JsonBuilder.isLenient] property. With `isLenient = true`, you can parse quite freely-formatted data:
+
+```kotlin
+val format = Json { isLenient = true }
+
+enum class Status { SUPPORTED }
+
+@Serializable
+data class Project(val name: String, val status: Status, val votes: Int)
+
+fun main() {
+ val data = format.decodeFromString<Project>("""
+ {
+ name : kotlinx.serialization,
+ status : SUPPORTED,
+ votes : "9000"
+ }
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-02.kt).
+
+You get the object, even though all keys of the source JSON, string, and enum values are unquoted, while an
+integer is quoted:
+
+```text
+Project(name=kotlinx.serialization, status=SUPPORTED, votes=9000)
+```
+
+<!--- TEST -->
+
+### Ignoring unknown keys
+
+JSON format is often used to read the output of third-party services or in other dynamic environments where
+new properties can be added during the API evolution. By default, unknown keys encountered during deserialization produce an error.
+You can avoid this and just ignore such keys by setting the [ignoreUnknownKeys][JsonBuilder.ignoreUnknownKeys] property
+to `true`:
+
+```kotlin
+val format = Json { ignoreUnknownKeys = true }
+
+@Serializable
+data class Project(val name: String)
+
+fun main() {
+ val data = format.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":"Kotlin"}
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-03.kt).
+
+It decodes the object despite the fact that the `Project` class doesn't have the `language` property:
+
+```text
+Project(name=kotlinx.serialization)
+```
+
+<!--- TEST -->
+
+### Alternative Json names
+
+It's not a rare case when JSON fields are renamed due to a schema version change.
+You can use the [`@SerialName` annotation](basic-serialization.md#serial-field-names) to change the name of a JSON field,
+but such renaming blocks the ability to decode data with the old name.
+To support multiple JSON names for the one Kotlin property, there is the [JsonNames] annotation:
+
+```kotlin
+@Serializable
+data class Project(@JsonNames("title") val name: String)
+
+fun main() {
+ val project = Json.decodeFromString<Project>("""{"name":"kotlinx.serialization"}""")
+ println(project)
+ val oldProject = Json.decodeFromString<Project>("""{"title":"kotlinx.coroutines"}""")
+ println(oldProject)
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-04.kt).
+
+As you can see, both `name` and `title` Json fields correspond to `name` property:
+
+```text
+Project(name=kotlinx.serialization)
+Project(name=kotlinx.coroutines)
+```
+
+Support for [JsonNames] annotation is controlled by the [JsonBuilder.useAlternativeNames] flag.
+Unlike most of the configuration flags, this one is enabled by default and does not need attention
+unless you want to do some fine-tuning.
+
+<!--- TEST -->
+
+### Coercing input values
+
+JSON formats that from third parties can evolve, sometimes changing the field types.
+This can lead to exceptions during decoding when the actual values do not match the expected values.
+The default [Json] implementation is strict with respect to input types as was demonstrated in
+the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section. You can relax this restriction
+using the [coerceInputValues][JsonBuilder.coerceInputValues] property.
+
+This property only affects decoding. It treats a limited subset of invalid input values as if the
+corresponding property was missing and uses the default value of the corresponding property instead.
+The current list of supported invalid values is:
+
+* `null` inputs for non-nullable types
+* unknown values for enums
+
+> This list may be expanded in the future, so that [Json] instance configured with this property becomes even more
+> permissive to invalid value in the input, replacing them with defaults.
+
+See the example from the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section:
+
+```kotlin
+val format = Json { coerceInputValues = true }
+
+@Serializable
+data class Project(val name: String, val language: String = "Kotlin")
+
+fun main() {
+ val data = format.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":null}
+ """)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-05.kt).
+
+The invalid `null` value for the `language` property was coerced into the default value:
+
+```text
+Project(name=kotlinx.serialization, language=Kotlin)
+```
+
+<!--- TEST -->
+
+
+### Encoding defaults
+
+Default values of properties are not encoded by default because they will be assigned to missing fields during decoding anyway.
+See the [Defaults are not encoded](basic-serialization.md#defaults-are-not-encoded) section for details and an example.
+This is especially useful for nullable properties with null defaults and avoids writing the corresponding null values.
+The default behavior can be changed by setting the [encodeDefaults][JsonBuilder.encodeDefaults] property to `true`:
+
+```kotlin
+val format = Json { encodeDefaults = true }
+
+@Serializable
+class Project(
+ val name: String,
+ val language: String = "Kotlin",
+ val website: String? = null
+)
+
+fun main() {
+ val data = Project("kotlinx.serialization")
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-06.kt).
+
+It produces the following output which encodes all the property values including the default ones:
+
+```text
+{"name":"kotlinx.serialization","language":"Kotlin","website":null}
+```
+
+<!--- TEST -->
+
+### Explicit nulls
+
+By default, all `null` values are encoded into JSON strings, but in some cases you may want to omit them.
+The encoding of `null` values can be controlled with the [explicitNulls][JsonBuilder.explicitNulls] property.
+
+If you set property to `false`, fields with `null` values are not encoded into JSON even if the property does not have a
+default `null` value. When decoding such JSON, the absence of a property value is treated as `null` for nullable properties
+without a default value.
+
+```kotlin
+val format = Json { explicitNulls = false }
+
+@Serializable
+data class Project(
+ val name: String,
+ val language: String,
+ val version: String? = "1.2.2",
+ val website: String?,
+ val description: String? = null
+)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin", null, null, null)
+ val json = format.encodeToString(data)
+ println(json)
+ println(format.decodeFromString<Project>(json))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-07.kt).
+
+As you can see, `version`, `website` and `description` fields are not present in output JSON on the first line.
+After decoding, the missing nullable property `website` without a default values has received a `null` value,
+while nullable properties `version` and `description` are filled with their default values:
+
+```text
+{"name":"kotlinx.serialization","language":"Kotlin"}
+Project(name=kotlinx.serialization, language=Kotlin, version=1.2.2, website=null, description=null)
+```
+
+`explicitNulls` is `true` by default as it is the default behavior across different versions of the library.
+
+<!--- TEST -->
+
+### Allowing structured map keys
+
+JSON format does not natively support the concept of a map with structured keys. Keys in JSON objects
+are strings and can be used to represent only primitives or enums by default.
+You can enable non-standard support for structured keys with
+the [allowStructuredMapKeys][JsonBuilder.allowStructuredMapKeys] property.
+
+This is how you can serialize a map with keys of a user-defined class:
+
+```kotlin
+val format = Json { allowStructuredMapKeys = true }
+
+@Serializable
+data class Project(val name: String)
+
+fun main() {
+ val map = mapOf(
+ Project("kotlinx.serialization") to "Serialization",
+ Project("kotlinx.coroutines") to "Coroutines"
+ )
+ println(format.encodeToString(map))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-08.kt).
+
+The map with structured keys gets represented as JSON array with the following items: `[key1, value1, key2, value2,...]`.
+
+```text
+[{"name":"kotlinx.serialization"},"Serialization",{"name":"kotlinx.coroutines"},"Coroutines"]
+```
+
+<!--- TEST -->
+
+### Allowing special floating-point values
+
+By default, special floating-point values like [Double.NaN] and infinities are not supported in JSON because
+the JSON specification prohibits it.
+You can enable their encoding using the [allowSpecialFloatingPointValues][JsonBuilder.allowSpecialFloatingPointValues]
+property:
+
+```kotlin
+val format = Json { allowSpecialFloatingPointValues = true }
+
+@Serializable
+class Data(
+ val value: Double
+)
+
+fun main() {
+ val data = Data(Double.NaN)
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-09.kt).
+
+This example produces the following non-stardard JSON output, yet it is a widely used encoding for
+special values in JVM world:
+
+```text
+{"value":NaN}
+```
+
+<!--- TEST -->
+
+### Class discriminator for polymorphism
+
+A key name that specifies a type when you have a polymorphic data can be specified
+in the [classDiscriminator][JsonBuilder.classDiscriminator] property:
+
+```kotlin
+val format = Json { classDiscriminator = "#class" }
+
+@Serializable
+sealed class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-10.kt).
+
+In combination with an explicitly specified [SerialName] of the class it provides full
+control over the resulting JSON object:
+
+```text
+{"#class":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
+```
+
+<!--- TEST -->
+
+It is also possible to specify different class discriminators for different hierarchies. Instead of Json instance property, use [JsonClassDiscriminator] annotation directly on base serializable class:
+
+```kotlin
+@Serializable
+@JsonClassDiscriminator("message_type")
+sealed class Base
+```
+
+This annotation is _inheritable_, so all subclasses of `Base` will have the same discriminator:
+
+```kotlin
+@Serializable // Class discriminator is inherited from Base
+sealed class ErrorClass: Base()
+```
+
+> To learn more about inheritable serial annotations, see documentation for [InheritableSerialInfo].
+
+Note that it is not possible to explicitly specify different class discriminators in subclasses of `Base`. Only hierarchies with empty intersections can have different discriminators.
+
+Discriminator specified in the annotation has priority over discriminator in Json configuration:
+
+<!--- INCLUDE
+
+@Serializable
+data class Message(val message: Base, val error: ErrorClass?)
+
+@Serializable
+@SerialName("my.app.BaseMessage")
+data class BaseMessage(val message: String) : Base()
+
+@Serializable
+@SerialName("my.app.GenericError")
+data class GenericError(@SerialName("error_code") val errorCode: Int) : ErrorClass()
+-->
+
+```kotlin
+
+val format = Json { classDiscriminator = "#class" }
+
+fun main() {
+ val data = Message(BaseMessage("not found"), GenericError(404))
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-11.kt).
+
+As you can see, discriminator from the `Base` class is used:
+
+```text
+{"message":{"message_type":"my.app.BaseMessage","message":"not found"},"error":{"message_type":"my.app.GenericError","error_code":404}}
+```
+
+<!--- TEST -->
+
+
+## Json elements
+
+Aside from direct conversions between strings and JSON objects, Kotlin serialization offers APIs that allow
+other ways of working with JSON in the code. For example, you might need to tweak the data before it can parse
+or otherwise work with such an unstructured data that it does not readily fit into the typesafe world of Kotlin
+serialization.
+
+The main concept in this part of the library is [JsonElement]. Read on to learn what you can do with it.
+
+### Parsing to Json element
+
+A string can be _parsed_ into an instance of [JsonElement] with the [Json.parseToJsonElement] function.
+It is called neither decoding nor deserialization because none of that happens in the process.
+It just parses a JSON and forms an object representing it:
+
+```kotlin
+fun main() {
+ val element = Json.parseToJsonElement("""
+ {"name":"kotlinx.serialization","language":"Kotlin"}
+ """)
+ println(element)
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-12.kt).
+
+A `JsonElement` prints itself as a valid JSON:
+
+```text
+{"name":"kotlinx.serialization","language":"Kotlin"}
+```
+
+<!--- TEST -->
+
+### Types of Json elements
+
+A [JsonElement] class has three direct subtypes, closely following JSON grammar:
+
+* [JsonPrimitive] represents primitive JSON elements, such as string, number, boolean, and null.
+ Each primitive has a simple string [content][JsonPrimitive.content]. There is also a
+ [JsonPrimitive()] constructor function overloaded to accept various primitive Kotlin types and
+ to convert them to `JsonPrimitive`.
+
+* [JsonArray] represents a JSON `[...]` array. It is a Kotlin [List] of `JsonElement` items.
+
+* [JsonObject] represents a JSON `{...}` object. It is a Kotlin [Map] from `String` keys to `JsonElement` values.
+
+The `JsonElement` class has extensions that cast it to its corresponding subtypes:
+[jsonPrimitive][_jsonPrimitive], [jsonArray][_jsonArray], [jsonObject][_jsonObject]. The `JsonPrimitive` class,
+in turn, provides converters to Kotlin primitive types: [int], [intOrNull], [long], [longOrNull],
+and similar ones for other types. This is how you can use them for processing JSON whose structure you know:
+
+```kotlin
+fun main() {
+ val element = Json.parseToJsonElement("""
+ {
+ "name": "kotlinx.serialization",
+ "forks": [{"votes": 42}, {"votes": 9000}, {}]
+ }
+ """)
+ val sum = element
+ .jsonObject["forks"]!!
+ .jsonArray.sumOf { it.jsonObject["votes"]?.jsonPrimitive?.int ?: 0 }
+ println(sum)
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-13.kt).
+
+The above example sums `votes` in all objects in the `forks` array, ignoring the objects that have no `votes`:
+
+```text
+9042
+```
+
+<!--- TEST -->
+
+Note that the execution will fail if the structure of the data is otherwise different.
+
+### Json element builders
+
+You can construct instances of specific [JsonElement] subtypes using the respective builder functions
+[buildJsonArray] and [buildJsonObject]. They provide a DSL to define the resulting JSON structure. It is
+is similar to Kotlin standard library collection builders, but with a JSON-specific convenience
+of more type-specific overloads and inner builder functions. The following example shows
+all the key features:
+
+```kotlin
+fun main() {
+ val element = buildJsonObject {
+ put("name", "kotlinx.serialization")
+ putJsonObject("owner") {
+ put("name", "kotlin")
+ }
+ putJsonArray("forks") {
+ addJsonObject {
+ put("votes", 42)
+ }
+ addJsonObject {
+ put("votes", 9000)
+ }
+ }
+ }
+ println(element)
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-14.kt).
+
+As a result, you get a proper JSON string:
+
+```text
+{"name":"kotlinx.serialization","owner":{"name":"kotlin"},"forks":[{"votes":42},{"votes":9000}]}
+```
+
+<!--- TEST -->
+
+### Decoding Json elements
+
+An instance of the [JsonElement] class can be decoded into a serializable object using
+the [Json.decodeFromJsonElement] function:
+
+```kotlin
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val element = buildJsonObject {
+ put("name", "kotlinx.serialization")
+ put("language", "Kotlin")
+ }
+ val data = Json.decodeFromJsonElement<Project>(element)
+ println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-15.kt).
+
+The result is exactly what you would expect:
+
+```text
+Project(name=kotlinx.serialization, language=Kotlin)
+```
+
+<!--- TEST -->
+
+## Json transformations
+
+To affect the shape and contents of JSON output after serialization, or adapt input to deserialization,
+it is possible to write a [custom serializer](serializers.md). However, it may be inconvenient to
+carefully follow [Encoder] and [Decoder] calling conventions, especially for relatively small and easy tasks.
+For that purpose, Kotlin serialization provides an API that can reduce the burden of implementing a custom
+serializer to a problem of manipulating a Json elements tree.
+
+We recommend that you get familiar with the [Serializers](serializers.md) chapter: among other things, it
+explains how custom serializers are bound to classes.
+
+Transformation capabilities are provided by the abstract [JsonTransformingSerializer] class which implements [KSerializer].
+Instead of direct interaction with `Encoder` or `Decoder`, this class asks you to supply transformations for JSON tree
+represented by the [JsonElement] class using the`transformSerialize` and
+`transformDeserialize` methods. Let's take a look at the examples.
+
+### Array wrapping
+
+The first example is an implementation of JSON array wrapping for lists.
+
+Consider a REST API that returns a JSON array of `User` objects, or a single object (not wrapped into an array) if there
+is only one element in the result.
+
+In the data model, use the [`@Serializable`][Serializable] annotation to specify a custom serializer for a
+`users: List<User>` property.
+
+<!--- INCLUDE
+import kotlinx.serialization.builtins.*
+-->
+
+```kotlin
+@Serializable
+data class Project(
+ val name: String,
+ @Serializable(with = UserListSerializer::class)
+ val users: List<User>
+)
+
+@Serializable
+data class User(val name: String)
+```
+
+Since this example covers only the deserialization case, you can implement `UserListSerializer` and override only the
+`transformDeserialize` function. The `JsonTransformingSerializer` constructor takes an original serializer
+as parameter (this approach is shown in the section [Constructing collection serializers](serializers.md#constructing-collection-serializers)):
+
+```kotlin
+object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) {
+ // If response is not an array, then it is a single object that should be wrapped into the array
+ override fun transformDeserialize(element: JsonElement): JsonElement =
+ if (element !is JsonArray) JsonArray(listOf(element)) else element
+}
+```
+
+Now you can test the code with a JSON array or a single JSON object as inputs.
+
+```kotlin
+fun main() {
+ println(Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","users":{"name":"kotlin"}}
+ """))
+ println(Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","users":[{"name":"kotlin"},{"name":"jetbrains"}]}
+ """))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-16.kt).
+
+The output shows that both cases are correctly deserialized into a Kotlin [List].
+
+```text
+Project(name=kotlinx.serialization, users=[User(name=kotlin)])
+Project(name=kotlinx.serialization, users=[User(name=kotlin), User(name=jetbrains)])
+```
+
+<!--- TEST -->
+
+### Array unwrapping
+
+You can also implement the `transformSerialize` function to unwrap a single-element list into a single JSON object
+during serialization:
+
+<!--- INCLUDE
+import kotlinx.serialization.builtins.*
+
+@Serializable
+data class Project(
+ val name: String,
+ @Serializable(with = UserListSerializer::class)
+ val users: List<User>
+)
+
+@Serializable
+data class User(val name: String)
+
+object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) {
+-->
+
+```kotlin
+ override fun transformSerialize(element: JsonElement): JsonElement {
+ require(element is JsonArray) // this serializer is used only with lists
+ return element.singleOrNull() ?: element
+ }
+```
+
+<!--- INCLUDE
+}
+-->
+
+Now, if you serialize a single-element list of objects from Kotlin:
+
+```kotlin
+fun main() {
+ val data = Project("kotlinx.serialization", listOf(User("kotlin")))
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-17.kt).
+
+You end up with a single JSON object, not an array with one element:
+
+```text
+{"name":"kotlinx.serialization","users":{"name":"kotlin"}}
+```
+
+<!--- TEST -->
+
+### Manipulating default values
+
+Another kind of useful transformation is omitting specific values from the output JSON, for example, if it
+is used as default when missing or for other reasons.
+
+Imagine that you cannot specify a default value for the `language` property in the `Project` data model for some reason,
+but you need it omitted from the JSON when it is equal to `Kotlin` (we can all agree that Kotlin should be default anyway).
+You can fix it by writing the special `ProjectSerializer` based on
+the [Plugin-generated serializer](serializers.md#plugin-generated-serializer) for the `Project` class.
+
+```kotlin
+@Serializable
+class Project(val name: String, val language: String)
+
+object ProjectSerializer : JsonTransformingSerializer<Project>(Project.serializer()) {
+ override fun transformSerialize(element: JsonElement): JsonElement =
+ // Filter out top-level key value pair with the key "language" and the value "Kotlin"
+ JsonObject(element.jsonObject.filterNot {
+ (k, v) -> k == "language" && v.jsonPrimitive.content == "Kotlin"
+ })
+}
+```
+
+In the example below, we are serializing the `Project` class at the top-level, so we explicitly
+pass the above `ProjectSerializer` to [Json.encodeToString] function as was shown in
+the [Passing a serializer manually](serializers.md#passing-a-serializer-manually) section:
+
+```kotlin
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(Json.encodeToString(data)) // using plugin-generated serializer
+ println(Json.encodeToString(ProjectSerializer, data)) // using custom serializer
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-18.kt).
+
+See the effect of the custom serializer:
+
+```text
+{"name":"kotlinx.serialization","language":"Kotlin"}
+{"name":"kotlinx.serialization"}
+```
+
+<!--- TEST -->
+
+### Content-based polymorphic deserialization
+
+Typically, [polymorphic serialization](polymorphism.md) requires a dedicated `"type"` key
+(also known as _class discriminator_) in the incoming JSON object to determine the actual serializer
+which should be used to deserialize Kotlin class.
+
+However, sometimes the `type` property may not be present in the input. In this case, you need to guess
+the actual type by the shape of JSON, for example by the presence of a specific key.
+
+[JsonContentPolymorphicSerializer] provides a skeleton implementation for such a strategy.
+To use it, override its `selectDeserializer` method.
+Let's start with the following class hierarchy.
+
+> Note that is does not have to be `sealed` as recommended in the [Sealed classes](polymorphism.md#sealed-classes) section,
+> because we are not going to take advantage of the plugin-generated code that automatically selects the
+> appropriate subclass, but are going to implement this code manually.
+
+<!--- INCLUDE
+import kotlinx.serialization.builtins.*
+-->
+
+```kotlin
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+data class BasicProject(override val name: String): Project()
+
+
+@Serializable
+data class OwnedProject(override val name: String, val owner: String) : Project()
+```
+
+You can distinguish the `BasicProject` and `OwnedProject` subclasses by the presence of
+the `owner` key in the JSON object.
+
+```kotlin
+object ProjectSerializer : JsonContentPolymorphicSerializer<Project>(Project::class) {
+ override fun selectDeserializer(element: JsonElement) = when {
+ "owner" in element.jsonObject -> OwnedProject.serializer()
+ else -> BasicProject.serializer()
+ }
+}
+```
+
+When you use this serializer to serialize data, either [registered](polymorphism.md#registered-subclasses) or
+the default serializer is selected for the actual type at runtime:
+
+```kotlin
+fun main() {
+ val data = listOf(
+ OwnedProject("kotlinx.serialization", "kotlin"),
+ BasicProject("example")
+ )
+ val string = Json.encodeToString(ListSerializer(ProjectSerializer), data)
+ println(string)
+ println(Json.decodeFromString(ListSerializer(ProjectSerializer), string))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-19.kt).
+
+No class discriminator is added in the JSON output:
+
+```text
+[{"name":"kotlinx.serialization","owner":"kotlin"},{"name":"example"}]
+[OwnedProject(name=kotlinx.serialization, owner=kotlin), BasicProject(name=example)]
+```
+
+<!--- TEST -->
+
+### Under the hood (experimental)
+
+Although abstract serializers mentioned above can cover most of the cases, it is possible to implement similar machinery
+manually, using only the [KSerializer] class.
+If tweaking the abstract methods `transformSerialize`/`transformDeserialize`/`selectDeserializer` is not enough,
+then altering `serialize`/`deserialize` is a way to go.
+
+Here are some useful things about custom serializers with [Json]:
+
+* [Encoder] can be cast to [JsonEncoder], and [Decoder] to [JsonDecoder], if the current format is [Json].
+* `JsonDecoder` has the [decodeJsonElement][JsonDecoder.decodeJsonElement] method and `JsonEncoder`
+ has the [encodeJsonElement][JsonEncoder.encodeJsonElement] method,
+ which basically retrieve an element from and insert an element to a current position in the stream.
+* Both [`JsonDecoder`][JsonDecoder.json] and [`JsonEncoder`][JsonEncoder.json] have the `json` property,
+ which returns [Json] instance with all settings that are currently in use.
+* [Json] has the [encodeToJsonElement][Json.encodeToJsonElement] and [decodeFromJsonElement][Json.decodeFromJsonElement] methods.
+
+Given all that, it is possible to implement two-stage conversion `Decoder -> JsonElement -> value` or
+`value -> JsonElement -> Encoder`.
+For example, you can implement a fully custom serializer for the following `Response` class so that its
+`Ok` subclass is represented directly, but the `Error` subclass is represented by an object with the error message:
+
+<!--- INCLUDE
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+-->
+
+```kotlin
+@Serializable(with = ResponseSerializer::class)
+sealed class Response<out T> {
+ data class Ok<out T>(val data: T) : Response<T>()
+ data class Error(val message: String) : Response<Nothing>()
+}
+
+class ResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Response<T>> {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("Response", PolymorphicKind.SEALED) {
+ element("Ok", buildClassSerialDescriptor("Ok") {
+ element<String>("message")
+ })
+ element("Error", dataSerializer.descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): Response<T> {
+ // Decoder -> JsonDecoder
+ require(decoder is JsonDecoder) // this class can be decoded only by Json
+ // JsonDecoder -> JsonElement
+ val element = decoder.decodeJsonElement()
+ // JsonElement -> value
+ if (element is JsonObject && "error" in element)
+ return Response.Error(element["error"]!!.jsonPrimitive.content)
+ return Response.Ok(decoder.json.decodeFromJsonElement(dataSerializer, element))
+ }
+
+ override fun serialize(encoder: Encoder, value: Response<T>) {
+ // Encoder -> JsonEncoder
+ require(encoder is JsonEncoder) // This class can be encoded only by Json
+ // value -> JsonElement
+ val element = when (value) {
+ is Response.Ok -> encoder.json.encodeToJsonElement(dataSerializer, value.data)
+ is Response.Error -> buildJsonObject { put("error", value.message) }
+ }
+ // JsonElement -> JsonEncoder
+ encoder.encodeJsonElement(element)
+ }
+}
+```
+
+Having this serializable `Response` implementation, you can take any serializable payload for its data
+and serialize or deserialize the corresponding responses:
+
+```kotlin
+@Serializable
+data class Project(val name: String)
+
+fun main() {
+ val responses = listOf(
+ Response.Ok(Project("kotlinx.serialization")),
+ Response.Error("Not found")
+ )
+ val string = Json.encodeToString(responses)
+ println(string)
+ println(Json.decodeFromString<List<Response<Project>>>(string))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-20.kt).
+
+This gives you fine-grained control on the representation of the `Response` class in the JSON output:
+
+```text
+[{"name":"kotlinx.serialization"},{"error":"Not found"}]
+[Ok(data=Project(name=kotlinx.serialization)), Error(message=Not found)]
+```
+
+<!--- TEST -->
+
+### Maintaining custom JSON attributes
+
+A good example of custom JSON-specific serializer would be a deserializer
+that packs all unknown JSON properties into a dedicated field of `JsonObject` type.
+
+Let's add `UnknownProject` &ndash; a class with the `name` property and arbitrary details flattened into the same object:
+
+<!--- INCLUDE
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+-->
+
+```kotlin
+data class UnknownProject(val name: String, val details: JsonObject)
+```
+
+However, the default plugin-generated serializer requires details
+to be a separate JSON object and that's not what we want.
+
+To mitigate that, write an own serializer that uses the fact that it works only with the `Json` format:
+
+```kotlin
+object UnknownProjectSerializer : KSerializer<UnknownProject> {
+ override val descriptor: SerialDescriptor = buildClassSerialDescriptor("UnknownProject") {
+ element<String>("name")
+ element<JsonElement>("details")
+ }
+
+ override fun deserialize(decoder: Decoder): UnknownProject {
+ // Cast to JSON-specific interface
+ val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON")
+ // Read the whole content as JSON
+ val json = jsonInput.decodeJsonElement().jsonObject
+ // Extract and remove name property
+ val name = json.getValue("name").jsonPrimitive.content
+ val details = json.toMutableMap()
+ details.remove("name")
+ return UnknownProject(name, JsonObject(details))
+ }
+
+ override fun serialize(encoder: Encoder, value: UnknownProject) {
+ error("Serialization is not supported")
+ }
+}
+```
+
+Now it can be used to read flattened JSON details as `UnknownProject`:
+
+```kotlin
+fun main() {
+ println(Json.decodeFromString(UnknownProjectSerializer, """{"type":"unknown","name":"example","maintainer":"Unknown","license":"Apache 2.0"}"""))
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-21.kt).
+
+```text
+UnknownProject(name=example, details={"type":"unknown","maintainer":"Unknown","license":"Apache 2.0"})
+```
+
+<!--- TEST -->
+
+---
+
+The next chapter covers [Alternative and custom formats (experimental)](formats.md).
+
+
+<!-- references -->
+[RFC-4627]: https://www.ietf.org/rfc/rfc4627.txt
+
+<!-- stdlib references -->
+[Double.NaN]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/-na-n.html
+[List]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/
+[Map]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/
+
+<!--- MODULE /kotlinx-serialization-core -->
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
+
+[SerialName]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html
+[InheritableSerialInfo]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-inheritable-serial-info/index.html
+[KSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html
+[Serializable]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding -->
+
+[Encoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html
+[Decoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html
+
+<!--- MODULE /kotlinx-serialization-json -->
+<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json -->
+
+[Json]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html
+[Json()]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json.html
+[JsonBuilder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/index.html
+[JsonBuilder.prettyPrint]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print.html
+[JsonBuilder.isLenient]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/is-lenient.html
+[JsonBuilder.ignoreUnknownKeys]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/ignore-unknown-keys.html
+[JsonNames]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-names/index.html
+[JsonBuilder.useAlternativeNames]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/use-alternative-names.html
+[JsonBuilder.coerceInputValues]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/coerce-input-values.html
+[JsonBuilder.encodeDefaults]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/encode-defaults.html
+[JsonBuilder.explicitNulls]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/explicit-nulls.html
+[JsonBuilder.allowStructuredMapKeys]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-structured-map-keys.html
+[JsonBuilder.allowSpecialFloatingPointValues]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-special-floating-point-values.html
+[JsonBuilder.classDiscriminator]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator.html
+[JsonClassDiscriminator]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-class-discriminator/index.html
+[JsonElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-element/index.html
+[Json.parseToJsonElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/parse-to-json-element.html
+[JsonPrimitive]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/index.html
+[JsonPrimitive.content]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/content.html
+[JsonPrimitive()]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive.html
+[JsonArray]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-array/index.html
+[JsonObject]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-object/index.html
+[_jsonPrimitive]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-primitive.html
+[_jsonArray]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-array.html
+[_jsonObject]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-object.html
+[int]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/int.html
+[intOrNull]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/int-or-null.html
+[long]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/long.html
+[longOrNull]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/long-or-null.html
+[buildJsonArray]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/build-json-array.html
+[buildJsonObject]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/build-json-object.html
+[Json.decodeFromJsonElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-json-element.html
+[JsonTransformingSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-transforming-serializer/index.html
+[Json.encodeToString]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html
+[JsonContentPolymorphicSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-content-polymorphic-serializer/index.html
+[JsonEncoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/index.html
+[JsonDecoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/index.html
+[JsonDecoder.decodeJsonElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/decode-json-element.html
+[JsonEncoder.encodeJsonElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/encode-json-element.html
+[JsonDecoder.json]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/json.html
+[JsonEncoder.json]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/json.html
+[Json.encodeToJsonElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-json-element.html
+
+<!--- END -->
diff --git a/docs/knit.properties b/docs/knit.properties
new file mode 100644
index 00000000..2915aa39
--- /dev/null
+++ b/docs/knit.properties
@@ -0,0 +1,2 @@
+knit.dir=../guide/example/
+test.dir=../guide/test/
diff --git a/docs/migration.md b/docs/migration.md
new file mode 100644
index 00000000..fb2cfd3c
--- /dev/null
+++ b/docs/migration.md
@@ -0,0 +1,19 @@
+# Migration from 0.20.0 version to 1.0.0
+
+For adopters of earlier versions of `kotlinx.serialization`, a dedicated migration path is prepared.
+During the preparation of serialization 1.0.0 release, most of the API has been changed, renamed, moved to
+a separate package or made internal. IDEA migrations were introduced, but unfortunately not all API can be migrated
+with automatic replacements.
+
+To simplify your migrations path, it is recommended to enable star imports in IDE (so all extensions are imported automatically) first.
+
+1. Update `kotlinx.serialization` to version `1.0.0-RC2` (this is the last version that has migrations for pre-1.0.0 versions. 1.0.0 version itself does not have any migration aids.)
+2. Rename dependency from `kotlinx-serialization-runtime` to `kotlinx-serialization-json`.
+3. For multiplatform usages, remove dependencies to platform-specific artifacts (e.g. `kotlinx-serialization-runtime-js`), they are [no longer required](/README.md#multiplatform-common-js-native) by Gradle.
+4. Update Kotlin to 1.4.0 or higher.
+5. Start applying replacements for the deprecated code.
+6. If some signatures are not resolved, try to hit `alt + Enter` and import the signature.
+7. If methods are still not resolved, it is recommended to use star imports for `kotlinx.serialization` signatures in the problematic file.
+8. When there are no usages of deprecated code left, you can change dependency version from `1.0.0-RC2` to `1.0.0`.
+
+For less trivial issues, it is recommended to study [the changelog](../CHANGELOG.md#100-rc--2020-08-17) or to ask for help in `#serialization` Kotlin's Slack channel.
diff --git a/docs/polymorphism.md b/docs/polymorphism.md
new file mode 100644
index 00000000..c41228aa
--- /dev/null
+++ b/docs/polymorphism.md
@@ -0,0 +1,1036 @@
+<!--- TEST_NAME PolymorphismTest -->
+
+# Polymorphism
+
+This is the fourth chapter of the [Kotlin Serialization Guide](serialization-guide.md).
+In this chapter we'll see how Kotlin Serialization deals with polymorphic class hierarchies.
+
+**Table of contents**
+
+<!--- TOC -->
+
+* [Closed polymorphism](#closed-polymorphism)
+ * [Static types](#static-types)
+ * [Designing serializable hierarchy](#designing-serializable-hierarchy)
+ * [Sealed classes](#sealed-classes)
+ * [Custom subclass serial name](#custom-subclass-serial-name)
+ * [Concrete properties in a base class](#concrete-properties-in-a-base-class)
+ * [Objects](#objects)
+* [Open polymorphism](#open-polymorphism)
+ * [Registered subclasses](#registered-subclasses)
+ * [Serializing interfaces](#serializing-interfaces)
+ * [Property of an interface type](#property-of-an-interface-type)
+ * [Static parent type lookup for polymorphism](#static-parent-type-lookup-for-polymorphism)
+ * [Explicitly marking polymorphic class properties](#explicitly-marking-polymorphic-class-properties)
+ * [Registering multiple superclasses](#registering-multiple-superclasses)
+ * [Polymorphism and generic classes](#polymorphism-and-generic-classes)
+ * [Merging library serializers modules](#merging-library-serializers-modules)
+ * [Default polymorphic type handler for deserialization](#default-polymorphic-type-handler-for-deserialization)
+ * [Default polymorphic type handler for serialization](#default-polymorphic-type-handler-for-serialization)
+
+<!--- END -->
+
+<!--- INCLUDE .*-poly-.*
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+-->
+
+## Closed polymorphism
+
+Let us start with basic introduction to polymorphism.
+
+### Static types
+
+Kotlin serialization is fully static with respect to types by default. The structure of encoded objects is determined
+by *compile-time* types of objects. Let's examine this aspect in more detail and learn how
+to serialize polymorphic data structures, where the type of data is determined at runtime.
+
+To show the static nature of Kotlin serialization let us make the following setup. An `open class Project`
+has just the `name` property, while its derived `class OwnedProject` adds an `owner` property.
+In the below example, we serialize `data` variable with a static type of
+`Project` that is initialized with an instance of `OwnedProject` at runtime.
+
+```kotlin
+@Serializable
+open class Project(val name: String)
+
+class OwnedProject(name: String, val owner: String) : Project(name)
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-01.kt).
+
+Despite the runtime type of `OwnedProject`, only the `Project` class properties are getting serialized.
+
+```text
+{"name":"kotlinx.coroutines"}
+```
+
+<!--- TEST -->
+
+Let's change the compile-time type of `data` to `OwnedProject`.
+
+```kotlin
+@Serializable
+open class Project(val name: String)
+
+class OwnedProject(name: String, val owner: String) : Project(name)
+
+fun main() {
+ val data = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-02.kt).
+
+We get an error, because the `OwnedProject` class is not serializable.
+
+```text
+Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'OwnedProject' is not found.
+Mark the class as @Serializable or provide the serializer explicitly.
+```
+
+<!--- TEST LINES_START -->
+
+### Designing serializable hierarchy
+
+We cannot simply mark `OwnedProject` from the previous example as `@Serializable`. It does not compile,
+running into the [constructor properties requirement](basic-serialization.md#constructor-properties-requirement).
+To make hierarchy of classes serializable, the properties in the parent class have to be marked `abstract`,
+making the `Project` class `abstract`, too.
+
+```kotlin
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-03.kt).
+
+This is close to the best design for a serializable hierarchy of classes, but running it produces the following error:
+
+```text
+Exception in thread "main" kotlinx.serialization.SerializationException: Class 'OwnedProject' is not registered for polymorphic serialization in the scope of 'Project'.
+Mark the base class as 'sealed' or register the serializer explicitly.
+```
+
+<!--- TEST LINES_START -->
+
+### Sealed classes
+
+The most straightforward way to use serialization with a polymorphic hierarchy is to mark the base class `sealed`.
+_All_ subclasses of a sealed class must be explicitly marked as `@Serializable`.
+
+```kotlin
+@Serializable
+sealed class Project {
+ abstract val name: String
+}
+
+@Serializable
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(Json.encodeToString(data)) // Serializing data of compile-time type Project
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-04.kt).
+
+Now we can see a default way to represent polymorphism in JSON.
+A `type` key is added to the resulting JSON object as a _discriminator_.
+
+```text
+{"type":"example.examplePoly04.OwnedProject","name":"kotlinx.coroutines","owner":"kotlin"}
+```
+
+<!--- TEST -->
+
+Pay attention to the small, but very important detail in the above example that is related to [Static types](#static-types):
+the `val data` property has a compile-time type of `Project`, even though its run-time type is `OwnedProject`.
+When serializing polymorphic class hierarchies you must ensure that the compile-time type of the serialized object
+is a polymorphic one, not a concrete one.
+
+Let us see what happens if the example is slightly changed, so that the compile-time of the object that is being
+serialized is `OwnedProject` (the same as its run-time type).
+
+```kotlin
+@Serializable
+sealed class Project {
+ abstract val name: String
+}
+
+@Serializable
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data = OwnedProject("kotlinx.coroutines", "kotlin") // data: OwnedProject here
+ println(Json.encodeToString(data)) // Serializing data of compile-time type OwnedProject
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-05.kt).
+
+The type of `OwnedProject` is concrete and is not polymorphic, thus the `type`
+discriminator property is not emitted into the resulting JSON.
+
+```text
+{"name":"kotlinx.coroutines","owner":"kotlin"}
+```
+
+<!--- TEST -->
+
+In general, Kotlin serialization is designed to work correctly only when the compile-time type used during serialization
+is the same one as the compile-time type used during deserialization. You can always specify the type explicitly
+when calling serialization functions. The previous example can be corrected to use `Project` type for serialization
+by calling `Json.encodeToString<Project>(data)`.
+
+### Custom subclass serial name
+
+A value of the `type` key is a fully qualified class name by default. We can put [SerialName] annotation onto
+the corresponding class to change it.
+
+```kotlin
+@Serializable
+sealed class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-06.kt).
+
+This way we can have a stable _serial name_ that is not affected by the class's name in the source code.
+
+```text
+{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
+```
+
+<!--- TEST -->
+
+> In addition to that, JSON can be configured to use a different key name for the class discriminator.
+> You can find an example in the [Class discriminator for polymorphism](json.md#class-discriminator-for-polymorphism) section.
+
+### Concrete properties in a base class
+
+A base class in a sealed hierarchy can have properties with backing fields.
+
+```kotlin
+@Serializable
+sealed class Project {
+ abstract val name: String
+ var status = "open"
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val json = Json { encodeDefaults = true } // "status" will be skipped otherwise
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-07.kt).
+
+The properties of the superclass are serialized before the properties of the subclass.
+
+```text
+{"type":"owned","status":"open","name":"kotlinx.coroutines","owner":"kotlin"}
+```
+
+<!--- TEST -->
+
+### Objects
+
+Sealed hierarchies can have objects as their subclasses and they also need to be marked as `@Serializable`.
+Let's take a different example with a hierarchy of `Response` classes.
+
+```kotlin
+@Serializable
+sealed class Response
+
+@Serializable
+object EmptyResponse : Response()
+
+@Serializable
+class TextResponse(val text: String) : Response()
+```
+
+Let us serialize a list of different responses.
+
+```kotlin
+fun main() {
+ val list = listOf(EmptyResponse, TextResponse("OK"))
+ println(Json.encodeToString(list))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-08.kt).
+
+An object serializes as an empty class, also using its fully-qualified class name as type by default:
+
+```text
+[{"type":"example.examplePoly08.EmptyResponse"},{"type":"example.examplePoly08.TextResponse","text":"OK"}]
+```
+
+<!--- TEST -->
+
+> Even if object has properties, they are not serialized.
+
+## Open polymorphism
+
+Serialization can work with arbitrary `open` classes or `abstract` classes.
+However, since this kind of polymorphism is open, there is a possibility that subclasses are defined anywhere in the
+source code, even in other modules, the list of subclasses that are serialized cannot be determined at compile-time and
+must be explicitly registered at runtime.
+
+### Registered subclasses
+
+Let us start with the code from the [Designing serializable hierarchy](#designing-serializable-hierarchy) section.
+To make it work with serialization without making it `sealed`, we have to define a [SerializersModule] using the
+[SerializersModule {}][SerializersModule()] builder function. In the module the base class is specified
+in the [polymorphic][_polymorphic] builder and each subclass is registered with the [subclass] function. Now,
+a custom JSON configuration can be instantiated with this module and used for serialization.
+
+> Details on custom JSON configurations can be found in
+> the [JSON configuration](json.md#json-configuration) section.
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+-->
+
+```kotlin
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-09.kt).
+
+This additional configuration makes our code work just as it worked with a sealed class in
+the [Sealed classes](#sealed-classes) section, but here subclasses can be spread arbitrarily throughout the code.
+
+```text
+{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
+```
+
+<!--- TEST -->
+>Please note that this example works only on JVM because of `serializer` function restrictions.
+>For JS and Native, explicit serializer should be used: `format.encodeToString(PolymorphicSerializer(Project::class), data)`
+>You can keep track of this issue [here](https://github.com/Kotlin/kotlinx.serialization/issues/1077).
+
+### Serializing interfaces
+
+We can update the previous example and turn `Project` superclass into an interface. However, we cannot
+mark an interface itself as `@Serializable`. No problem. Interfaces cannot have instances by themselves.
+Interfaces can only be represented by instances of their derived classes. Interfaces are used in the Kotlin language to enable polymorphism,
+so all interfaces are considered to be implicitly serializable with the [PolymorphicSerializer]
+strategy. We just need to mark their implementing classes as `@Serializable` and register them.
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+-->
+
+```kotlin
+interface Project {
+ val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project
+```
+
+Now if we declare `data` with the type of `Project` we can simply call `format.encodeToString` as before.
+
+```kotlin
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-10.kt).
+
+```text
+{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
+```
+
+<!--- TEST LINES_START -->
+
+### Property of an interface type
+
+Continuing the previous example, let us see what happens if we use `Project` interface as a property in some
+other serializable class. Interfaces are implicitly polymorphic, so we can just declare a property of an interface type.
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+interface Project {
+ val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project
+-->
+
+```kotlin
+@Serializable
+class Data(val project: Project) // Project is an interface
+
+fun main() {
+ val data = Data(OwnedProject("kotlinx.coroutines", "kotlin"))
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-11.kt).
+
+As long as we've registered the actual subtype of the interface that is being serialized in
+the [SerializersModule] of our `format`, we get it working at runtime.
+
+```text
+{"project":{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}}
+```
+
+<!--- TEST -->
+
+### Static parent type lookup for polymorphism
+
+During serialization of a polymorphic class the root type of the polymorphic hierarchy (`Project` in our example)
+is determined statically. Let us take the example with the serializable `abstract class Project`,
+but change the `main` function to declare `data` as having a type of `Any`:
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+-->
+
+```kotlin
+fun main() {
+ val data: Any = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-12.kt).
+
+We get the exception.
+
+```text
+Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Any' is not found.
+Mark the class as @Serializable or provide the serializer explicitly.
+```
+
+<!--- TEST LINES_START -->
+
+We have to register classes for polymorphic serialization with respect for the corresponding static type we
+use in the source code. First of all, we change our module to register a subclass of `Any`:
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+-->
+
+```kotlin
+val module = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(OwnedProject::class)
+ }
+}
+```
+
+<!--- INCLUDE
+val format = Json { serializersModule = module }
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+-->
+
+Then we can try to serialize the variable of type `Any`:
+
+```kotlin
+fun main() {
+ val data: Any = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-13.kt).
+
+However, the `Any` is a class and it is not serializable:
+
+```text
+Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Any' is not found.
+Mark the class as @Serializable or provide the serializer explicitly.
+```
+
+<!--- TEST LINES_START -->
+
+We must to explicitly pass an instance of [PolymorphicSerializer] for the base class `Any` as the
+first parameter to the [encodeToString][Json.encodeToString] function.
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+-->
+
+```kotlin
+fun main() {
+ val data: Any = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(PolymorphicSerializer(Any::class), data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-14.kt).
+
+With the explicit serializer it works as before.
+
+```text
+{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
+```
+
+<!--- TEST -->
+
+### Explicitly marking polymorphic class properties
+
+The property of an interface type is implicitly considered polymorphic, since interfaces are all about runtime polymorphism.
+However, Kotlin serialization does not compile a serializable class with a property of a non-serializable class type.
+If we have a property of `Any` class or other non-serializable class, then we must explicitly provide its serialization
+strategy via the [`@Serializable`][Serializable] annotation as we saw in
+the [Specifying serializer on a property](serializers.md#specifying-serializer-on-a-property) section.
+To specify a polymorphic serialization strategy of a property, the special-purpose [`@Polymorphic`][Polymorphic]
+annotation is used.
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+interface Project {
+ val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project
+-->
+
+```kotlin
+@Serializable
+class Data(
+ @Polymorphic // the code does not compile without it
+ val project: Any
+)
+
+fun main() {
+ val data = Data(OwnedProject("kotlinx.coroutines", "kotlin"))
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-15.kt).
+
+<!--- TEST
+{"project":{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}}
+-->
+
+### Registering multiple superclasses
+
+When the same class gets serialized as a value of properties with different compile-time type from the list of
+its superclasses, we must register it in the [SerializersModule] for each of its superclasses separately.
+It is convenient to extract registration of all the subclasses into a separate function and
+use it for each superclass. You can use the following template to write it.
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+import kotlin.reflect.KClass
+-->
+
+```kotlin
+val module = SerializersModule {
+ fun PolymorphicModuleBuilder<Project>.registerProjectSubclasses() {
+ subclass(OwnedProject::class)
+ }
+ polymorphic(Any::class) { registerProjectSubclasses() }
+ polymorphic(Project::class) { registerProjectSubclasses() }
+}
+```
+
+<!--- INCLUDE
+
+val format = Json { serializersModule = module }
+
+interface Project {
+ val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project
+
+@Serializable
+class Data(
+ val project: Project,
+ @Polymorphic val any: Any
+)
+
+fun main() {
+ val project = OwnedProject("kotlinx.coroutines", "kotlin")
+ val data = Data(project, project)
+ println(format.encodeToString(data))
+}
+-->
+
+> You can get the full code [here](../guide/example/example-poly-16.kt).
+
+<!--- TEST
+{"project":{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"},"any":{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}}
+-->
+
+### Polymorphism and generic classes
+
+Generic subtypes for a serializable class require a special handling. Consider the following hierarchy.
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+-->
+
+```kotlin
+@Serializable
+abstract class Response<out T>
+
+@Serializable
+@SerialName("OkResponse")
+data class OkResponse<out T>(val data: T) : Response<T>()
+```
+
+Kotlin serialization does not have a builtin strategy to represent the actually provided argument type for the
+type parameter `T` when serializing a property of the polymorphic type `OkResponse<T>`. We have to provide this
+strategy explicitly when defining the serializers module for the `Response`. In the below example we
+use `OkResponse.serializer(...)` to retrieve
+the [Plugin-generated generic serializer](serializers.md#plugin-generated-generic-serializer) of
+the `OkResponse` class and instantiate it with the [PolymorphicSerializer] instance with
+`Any` class as its base. This way, we can serialize an instance of `OkResponse` with any `data` property that
+was polymorphically registered as a subtype of `Any`.
+
+```kotlin
+val responseModule = SerializersModule {
+ polymorphic(Response::class) {
+ subclass(OkResponse.serializer(PolymorphicSerializer(Any::class)))
+ }
+}
+```
+
+### Merging library serializers modules
+
+When the application grows in size and splits into source code modules,
+it may become inconvenient to store all class hierarchies in one serializers module.
+Let us add a library with the `Project` hierarchy to the code from the previous section.
+
+```kotlin
+val projectModule = SerializersModule {
+ fun PolymorphicModuleBuilder<Project>.registerProjectSubclasses() {
+ subclass(OwnedProject::class)
+ }
+ polymorphic(Any::class) { registerProjectSubclasses() }
+ polymorphic(Project::class) { registerProjectSubclasses() }
+}
+```
+
+<!--- INCLUDE
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("OwnedProject")
+data class OwnedProject(override val name: String, val owner: String) : Project()
+-->
+
+We can compose those two modules together using the [plus] operator to merge them,
+so that we can use them both in the same [Json] format instance.
+
+> You can also use the [include][SerializersModuleBuilder.include] function
+> in the [SerializersModule {}][SerializersModule()] DSL.
+
+```kotlin
+val format = Json { serializersModule = projectModule + responseModule }
+````
+
+Now classes from both hierarchies can be serialized together and deserialized together.
+
+```kotlin
+fun main() {
+ // both Response and Project are abstract and their concrete subtypes are being serialized
+ val data: Response<Project> = OkResponse(OwnedProject("kotlinx.serialization", "kotlin"))
+ val string = format.encodeToString(data)
+ println(string)
+ println(format.decodeFromString<Response<Project>>(string))
+}
+
+```
+
+> You can get the full code [here](../guide/example/example-poly-17.kt).
+
+The JSON that is being produced is deeply polymorphic.
+
+```text
+{"type":"OkResponse","data":{"type":"OwnedProject","name":"kotlinx.serialization","owner":"kotlin"}}
+OkResponse(data=OwnedProject(name=kotlinx.serialization, owner=kotlin))
+```
+
+<!--- TEST -->
+
+If you're writing a library or shared module with an abstract class and some implementations of it,
+you can expose your own serializers module for your clients to use so that a client can combine your
+module with their modules.
+
+### Default polymorphic type handler for deserialization
+
+What happens when we deserialize a subclass that was not registered?
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("OwnedProject")
+data class OwnedProject(override val name: String, val owner: String) : Project()
+
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+-->
+
+```kotlin
+fun main() {
+ println(format.decodeFromString<Project>("""
+ {"type":"unknown","name":"example"}
+ """))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-18.kt).
+
+We get the following exception.
+
+```text
+Exception in thread "main" kotlinx.serialization.json.internal.JsonDecodingException: Polymorphic serializer was not found for class discriminator 'unknown'
+```
+
+<!--- TEST LINES_START -->
+
+When reading a flexible input we might want to provide some default behavior in this case. For example,
+we can have a `BasicProject` subtype to represent all kinds of unknown `Project` subtypes.
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+-->
+
+```kotlin
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+data class BasicProject(override val name: String, val type: String): Project()
+
+@Serializable
+@SerialName("OwnedProject")
+data class OwnedProject(override val name: String, val owner: String) : Project()
+```
+
+We register a default deserializer handler using the [`defaultDeserializer`][PolymorphicModuleBuilder.defaultDeserializer] function in
+the [`polymorphic { ... }`][PolymorphicModuleBuilder] DSL that defines a strategy which maps the `type` string from the input
+to the [deserialization strategy][DeserializationStrategy]. In the below example we don't use the type,
+but always return the [Plugin-generated serializer](serializers.md#plugin-generated-serializer)
+of the `BasicProject` class.
+
+```kotlin
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ defaultDeserializer { BasicProject.serializer() }
+ }
+}
+```
+
+Using this module we can now deserialize both instances of the registered `OwnedProject` and
+any unregistered one.
+
+```kotlin
+val format = Json { serializersModule = module }
+
+fun main() {
+ println(format.decodeFromString<List<Project>>("""
+ [
+ {"type":"unknown","name":"example"},
+ {"type":"OwnedProject","name":"kotlinx.serialization","owner":"kotlin"}
+ ]
+ """))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-19.kt).
+
+Notice, how `BasicProject` had also captured the specified type key in its `type` property.
+
+```text
+[BasicProject(name=example, type=unknown), OwnedProject(name=kotlinx.serialization, owner=kotlin)]
+```
+
+<!--- TEST -->
+
+We used a plugin-generated serializer as a default serializer, implying that
+the structure of the "unknown" data is known in advance. In a real-world API it's rarely the case.
+For that purpose a custom, less-structured serializer is needed. You will see the example of such serializer in the future section
+on [Maintaining custom JSON attributes](json.md#maintaining-custom-json-attributes).
+
+### Default polymorphic type handler for serialization
+
+Sometimes you need to dynamically choose which serializer to use for a polymorphic type based on the instance, for example if you
+don't have access to the full type hierarchy, or if it changes a lot. For this situation, you can register a default serializer.
+
+<!--- INCLUDE
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+-->
+
+```kotlin
+interface Animal {
+}
+
+interface Cat : Animal {
+ val catType: String
+}
+
+interface Dog : Animal {
+ val dogType: String
+}
+
+private class CatImpl : Cat {
+ override val catType: String = "Tabby"
+}
+
+private class DogImpl : Dog {
+ override val dogType: String = "Husky"
+}
+
+object AnimalProvider {
+ fun createCat(): Cat = CatImpl()
+ fun createDog(): Dog = DogImpl()
+}
+```
+
+We register a default serializer handler using the [`polymorphicDefaultSerializer`][SerializersModuleBuilder.polymorphicDefaultSerializer] function in
+the [`SerializersModule { ... }`][SerializersModuleBuilder] DSL that defines a strategy which takes an instance of the base class and
+provides a [serialization strategy][SerializationStrategy]. In the below example we use a `when` block to check the type of the
+instance, without ever having to refer to the private implementation classes.
+
+```kotlin
+val module = SerializersModule {
+ polymorphicDefaultSerializer(Animal::class) { instance ->
+ @Suppress("UNCHECKED_CAST")
+ when (instance) {
+ is Cat -> CatSerializer as SerializationStrategy<Animal>
+ is Dog -> DogSerializer as SerializationStrategy<Animal>
+ else -> null
+ }
+ }
+}
+
+object CatSerializer : SerializationStrategy<Cat> {
+ override val descriptor = buildClassSerialDescriptor("Cat") {
+ element<String>("catType")
+ }
+
+ override fun serialize(encoder: Encoder, value: Cat) {
+ encoder.encodeStructure(descriptor) {
+ encodeStringElement(descriptor, 0, value.catType)
+ }
+ }
+}
+
+object DogSerializer : SerializationStrategy<Dog> {
+ override val descriptor = buildClassSerialDescriptor("Dog") {
+ element<String>("dogType")
+ }
+
+ override fun serialize(encoder: Encoder, value: Dog) {
+ encoder.encodeStructure(descriptor) {
+ encodeStringElement(descriptor, 0, value.dogType)
+ }
+ }
+}
+```
+
+Using this module we can now serialize instances of `Cat` and `Dog`.
+
+```kotlin
+val format = Json { serializersModule = module }
+
+fun main() {
+ println(format.encodeToString<Animal>(AnimalProvider.createCat()))
+}
+```
+
+> You can get the full code [here](../guide/example/example-poly-20.kt)
+
+```text
+{"type":"Cat","catType":"Tabby"}
+```
+
+
+<!--- TEST -->
+
+---
+
+The next chapter covers [JSON features](json.md).
+
+<!--- MODULE /kotlinx-serialization-core -->
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
+
+[SerialName]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html
+[PolymorphicSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-polymorphic-serializer/index.html
+[Serializable]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html
+[Polymorphic]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-polymorphic/index.html
+[DeserializationStrategy]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-deserialization-strategy/index.html
+[SerializationStrategy]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serialization-strategy/index.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.modules -->
+
+[SerializersModule]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module/index.html
+[SerializersModule()]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module.html
+[_polymorphic]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/polymorphic.html
+[subclass]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/subclass.html
+[plus]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/plus.html
+[SerializersModuleBuilder.include]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module-builder/include.html
+[PolymorphicModuleBuilder.defaultDeserializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-polymorphic-module-builder/default-deserializer.html
+[PolymorphicModuleBuilder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-polymorphic-module-builder/index.html
+[SerializersModuleBuilder.polymorphicDefaultSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module-builder/polymorphic-default-serializer.html
+[SerializersModuleBuilder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module-builder/index.html
+
+<!--- MODULE /kotlinx-serialization-json -->
+<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json -->
+
+[Json.encodeToString]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html
+[Json]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html
+
+<!--- END -->
+
diff --git a/docs/serialization-guide.md b/docs/serialization-guide.md
new file mode 100644
index 00000000..e3f28eae
--- /dev/null
+++ b/docs/serialization-guide.md
@@ -0,0 +1,162 @@
+# Kotlin Serialization Guide
+
+Kotlin Serialization is a cross-platform and multi-format framework for data serialization&mdash;converting
+trees of objects to strings, byte arrays, or other _serial_ representations and back.
+Kotlin Serialization fully supports and enforces the Kotlin type system, making sure only valid
+objects can be deserialized.
+
+Kotlin Serialization is not just a library. It is a compiler plugin that is bundled with the Kotlin
+compiler distribution itself. Build configuration is explained in [README.md](../README.md#setup).
+Once the project is set up, we can start serializing some classes.
+
+## Table of contents
+
+**Chapter 1.** [Basic Serialization](basic-serialization.md) (**start reading here**)
+<!--- TOC_REF basic-serialization.md -->
+* <a name='basics'></a>[Basics](basic-serialization.md#basics)
+ * <a name='json-encoding'></a>[JSON encoding](basic-serialization.md#json-encoding)
+ * <a name='json-decoding'></a>[JSON decoding](basic-serialization.md#json-decoding)
+* <a name='serializable-classes'></a>[Serializable classes](basic-serialization.md#serializable-classes)
+ * <a name='backing-fields-are-serialized'></a>[Backing fields are serialized](basic-serialization.md#backing-fields-are-serialized)
+ * <a name='constructor-properties-requirement'></a>[Constructor properties requirement](basic-serialization.md#constructor-properties-requirement)
+ * <a name='data-validation'></a>[Data validation](basic-serialization.md#data-validation)
+ * <a name='optional-properties'></a>[Optional properties](basic-serialization.md#optional-properties)
+ * <a name='optional-property-initializer-call'></a>[Optional property initializer call](basic-serialization.md#optional-property-initializer-call)
+ * <a name='required-properties'></a>[Required properties](basic-serialization.md#required-properties)
+ * <a name='transient-properties'></a>[Transient properties](basic-serialization.md#transient-properties)
+ * <a name='defaults-are-not-encoded-by-default'></a>[Defaults are not encoded by default](basic-serialization.md#defaults-are-not-encoded-by-default)
+ * <a name='nullable-properties'></a>[Nullable properties](basic-serialization.md#nullable-properties)
+ * <a name='type-safety-is-enforced'></a>[Type safety is enforced](basic-serialization.md#type-safety-is-enforced)
+ * <a name='referenced-objects'></a>[Referenced objects](basic-serialization.md#referenced-objects)
+ * <a name='no-compression-of-repeated-references'></a>[No compression of repeated references](basic-serialization.md#no-compression-of-repeated-references)
+ * <a name='generic-classes'></a>[Generic classes](basic-serialization.md#generic-classes)
+ * <a name='serial-field-names'></a>[Serial field names](basic-serialization.md#serial-field-names)
+<!--- END -->
+
+**Chapter 2.** [Builtin Classes](builtin-classes.md)
+
+<!--- TOC_REF builtin-classes.md -->
+* <a name='primitives'></a>[Primitives](builtin-classes.md#primitives)
+ * <a name='numbers'></a>[Numbers](builtin-classes.md#numbers)
+ * <a name='long-numbers'></a>[Long numbers](builtin-classes.md#long-numbers)
+ * <a name='long-numbers-as-strings'></a>[Long numbers as strings](builtin-classes.md#long-numbers-as-strings)
+ * <a name='enum-classes'></a>[Enum classes](builtin-classes.md#enum-classes)
+ * <a name='serial-names-of-enum-entries'></a>[Serial names of enum entries](builtin-classes.md#serial-names-of-enum-entries)
+* <a name='composites'></a>[Composites](builtin-classes.md#composites)
+ * <a name='pair-and-triple'></a>[Pair and triple](builtin-classes.md#pair-and-triple)
+ * <a name='lists'></a>[Lists](builtin-classes.md#lists)
+ * <a name='sets-and-other-collections'></a>[Sets and other collections](builtin-classes.md#sets-and-other-collections)
+ * <a name='deserializing-collections'></a>[Deserializing collections](builtin-classes.md#deserializing-collections)
+ * <a name='maps'></a>[Maps](builtin-classes.md#maps)
+ * <a name='unit-and-singleton-objects'></a>[Unit and singleton objects](builtin-classes.md#unit-and-singleton-objects)
+<!--- END -->
+
+**Chapter 3.** [Serializers](serializers.md)
+
+<!--- TOC_REF serializers.md -->
+* <a name='introduction-to-serializers'></a>[Introduction to serializers](serializers.md#introduction-to-serializers)
+ * <a name='plugin-generated-serializer'></a>[Plugin-generated serializer](serializers.md#plugin-generated-serializer)
+ * <a name='plugin-generated-generic-serializer'></a>[Plugin-generated generic serializer](serializers.md#plugin-generated-generic-serializer)
+ * <a name='builtin-primitive-serializers'></a>[Builtin primitive serializers](serializers.md#builtin-primitive-serializers)
+ * <a name='constructing-collection-serializers'></a>[Constructing collection serializers](serializers.md#constructing-collection-serializers)
+ * <a name='using-top-level-serializer-function'></a>[Using top-level serializer function](serializers.md#using-top-level-serializer-function)
+* <a name='custom-serializers'></a>[Custom serializers](serializers.md#custom-serializers)
+ * <a name='primitive-serializer'></a>[Primitive serializer](serializers.md#primitive-serializer)
+ * <a name='delegating-serializers'></a>[Delegating serializers](serializers.md#delegating-serializers)
+ * <a name='composite-serializer-via-surrogate'></a>[Composite serializer via surrogate](serializers.md#composite-serializer-via-surrogate)
+ * <a name='hand-written-composite-serializer'></a>[Hand-written composite serializer](serializers.md#hand-written-composite-serializer)
+ * <a name='sequential-decoding-protocol-experimental'></a>[Sequential decoding protocol (experimental)](serializers.md#sequential-decoding-protocol-experimental)
+ * <a name='serializing-3rd-party-classes'></a>[Serializing 3rd party classes](serializers.md#serializing-3rd-party-classes)
+ * <a name='passing-a-serializer-manually'></a>[Passing a serializer manually](serializers.md#passing-a-serializer-manually)
+ * <a name='specifying-serializer-on-a-property'></a>[Specifying serializer on a property](serializers.md#specifying-serializer-on-a-property)
+ * <a name='specifying-serializers-for-a-file'></a>[Specifying serializers for a file](serializers.md#specifying-serializers-for-a-file)
+ * <a name='custom-serializers-for-a-generic-type'></a>[Custom serializers for a generic type](serializers.md#custom-serializers-for-a-generic-type)
+ * <a name='format-specific-serializers'></a>[Format-specific serializers](serializers.md#format-specific-serializers)
+* <a name='contextual-serialization'></a>[Contextual serialization](serializers.md#contextual-serialization)
+ * <a name='serializers-module'></a>[Serializers module](serializers.md#serializers-module)
+ * <a name='contextual-serialization-and-generic-classes'></a>[Contextual serialization and generic classes](serializers.md#contextual-serialization-and-generic-classes)
+* <a name='deriving-external-serializer-for-another-kotlin-class-experimental'></a>[Deriving external serializer for another Kotlin class (experimental)](serializers.md#deriving-external-serializer-for-another-kotlin-class-experimental)
+ * <a name='external-serialization-uses-properties'></a>[External serialization uses properties](serializers.md#external-serialization-uses-properties)
+<!--- END -->
+
+**Chapter 4.** [Polymorphism](polymorphism.md)
+
+<!--- TOC_REF polymorphism.md -->
+* <a name='closed-polymorphism'></a>[Closed polymorphism](polymorphism.md#closed-polymorphism)
+ * <a name='static-types'></a>[Static types](polymorphism.md#static-types)
+ * <a name='designing-serializable-hierarchy'></a>[Designing serializable hierarchy](polymorphism.md#designing-serializable-hierarchy)
+ * <a name='sealed-classes'></a>[Sealed classes](polymorphism.md#sealed-classes)
+ * <a name='custom-subclass-serial-name'></a>[Custom subclass serial name](polymorphism.md#custom-subclass-serial-name)
+ * <a name='concrete-properties-in-a-base-class'></a>[Concrete properties in a base class](polymorphism.md#concrete-properties-in-a-base-class)
+ * <a name='objects'></a>[Objects](polymorphism.md#objects)
+* <a name='open-polymorphism'></a>[Open polymorphism](polymorphism.md#open-polymorphism)
+ * <a name='registered-subclasses'></a>[Registered subclasses](polymorphism.md#registered-subclasses)
+ * <a name='serializing-interfaces'></a>[Serializing interfaces](polymorphism.md#serializing-interfaces)
+ * <a name='property-of-an-interface-type'></a>[Property of an interface type](polymorphism.md#property-of-an-interface-type)
+ * <a name='static-parent-type-lookup-for-polymorphism'></a>[Static parent type lookup for polymorphism](polymorphism.md#static-parent-type-lookup-for-polymorphism)
+ * <a name='explicitly-marking-polymorphic-class-properties'></a>[Explicitly marking polymorphic class properties](polymorphism.md#explicitly-marking-polymorphic-class-properties)
+ * <a name='registering-multiple-superclasses'></a>[Registering multiple superclasses](polymorphism.md#registering-multiple-superclasses)
+ * <a name='polymorphism-and-generic-classes'></a>[Polymorphism and generic classes](polymorphism.md#polymorphism-and-generic-classes)
+ * <a name='merging-library-serializers-modules'></a>[Merging library serializers modules](polymorphism.md#merging-library-serializers-modules)
+ * <a name='default-polymorphic-type-handler-for-deserialization'></a>[Default polymorphic type handler for deserialization](polymorphism.md#default-polymorphic-type-handler-for-deserialization)
+ * <a name='default-polymorphic-type-handler-for-serialization'></a>[Default polymorphic type handler for serialization](polymorphism.md#default-polymorphic-type-handler-for-serialization)
+<!--- END -->
+
+**Chapter 5.** [JSON Features](json.md)
+
+<!--- TOC_REF json.md -->
+* <a name='json-configuration'></a>[Json configuration](json.md#json-configuration)
+ * <a name='pretty-printing'></a>[Pretty printing](json.md#pretty-printing)
+ * <a name='lenient-parsing'></a>[Lenient parsing](json.md#lenient-parsing)
+ * <a name='ignoring-unknown-keys'></a>[Ignoring unknown keys](json.md#ignoring-unknown-keys)
+ * <a name='alternative-json-names'></a>[Alternative Json names](json.md#alternative-json-names)
+ * <a name='coercing-input-values'></a>[Coercing input values](json.md#coercing-input-values)
+ * <a name='encoding-defaults'></a>[Encoding defaults](json.md#encoding-defaults)
+ * <a name='explicit-nulls'></a>[Explicit nulls](json.md#explicit-nulls)
+ * <a name='allowing-structured-map-keys'></a>[Allowing structured map keys](json.md#allowing-structured-map-keys)
+ * <a name='allowing-special-floating-point-values'></a>[Allowing special floating-point values](json.md#allowing-special-floating-point-values)
+ * <a name='class-discriminator-for-polymorphism'></a>[Class discriminator for polymorphism](json.md#class-discriminator-for-polymorphism)
+* <a name='json-elements'></a>[Json elements](json.md#json-elements)
+ * <a name='parsing-to-json-element'></a>[Parsing to Json element](json.md#parsing-to-json-element)
+ * <a name='types-of-json-elements'></a>[Types of Json elements](json.md#types-of-json-elements)
+ * <a name='json-element-builders'></a>[Json element builders](json.md#json-element-builders)
+ * <a name='decoding-json-elements'></a>[Decoding Json elements](json.md#decoding-json-elements)
+* <a name='json-transformations'></a>[Json transformations](json.md#json-transformations)
+ * <a name='array-wrapping'></a>[Array wrapping](json.md#array-wrapping)
+ * <a name='array-unwrapping'></a>[Array unwrapping](json.md#array-unwrapping)
+ * <a name='manipulating-default-values'></a>[Manipulating default values](json.md#manipulating-default-values)
+ * <a name='content-based-polymorphic-deserialization'></a>[Content-based polymorphic deserialization](json.md#content-based-polymorphic-deserialization)
+ * <a name='under-the-hood-experimental'></a>[Under the hood (experimental)](json.md#under-the-hood-experimental)
+ * <a name='maintaining-custom-json-attributes'></a>[Maintaining custom JSON attributes](json.md#maintaining-custom-json-attributes)
+<!--- END -->
+
+**Chapter 6.** [Alternative and custom formats (experimental)](formats.md)
+
+<!--- TOC_REF formats.md -->
+* <a name='cbor-experimental'></a>[CBOR (experimental)](formats.md#cbor-experimental)
+ * <a name='ignoring-unknown-keys'></a>[Ignoring unknown keys](formats.md#ignoring-unknown-keys)
+ * <a name='byte-arrays-and-cbor-data-types'></a>[Byte arrays and CBOR data types](formats.md#byte-arrays-and-cbor-data-types)
+* <a name='protobuf-experimental'></a>[ProtoBuf (experimental)](formats.md#protobuf-experimental)
+ * <a name='field-numbers'></a>[Field numbers](formats.md#field-numbers)
+ * <a name='integer-types'></a>[Integer types](formats.md#integer-types)
+ * <a name='lists-as-repeated-fields'></a>[Lists as repeated fields](formats.md#lists-as-repeated-fields)
+ * <a name='packed-fields'></a>[Packed fields](formats.md#packed-fields)
+ * <a name='protobuf-schema-generator-experimental'></a>[ProtoBuf schema generator (experimental)](formats.md#protobuf-schema-generator-experimental)
+* <a name='properties-experimental'></a>[Properties (experimental)](formats.md#properties-experimental)
+* <a name='custom-formats-experimental'></a>[Custom formats (experimental)](formats.md#custom-formats-experimental)
+ * <a name='basic-encoder'></a>[Basic encoder](formats.md#basic-encoder)
+ * <a name='basic-decoder'></a>[Basic decoder](formats.md#basic-decoder)
+ * <a name='sequential-decoding'></a>[Sequential decoding](formats.md#sequential-decoding)
+ * <a name='adding-collection-support'></a>[Adding collection support](formats.md#adding-collection-support)
+ * <a name='adding-null-support'></a>[Adding null support](formats.md#adding-null-support)
+ * <a name='efficient-binary-format'></a>[Efficient binary format](formats.md#efficient-binary-format)
+ * <a name='format-specific-types'></a>[Format-specific types](formats.md#format-specific-types)
+<!--- END -->
+
+**Appendix A.** [Serialization and inline classes (experimental, IR-specific)](inline-classes.md)
+
+<!--- TOC_REF inline-classes.md -->
+* <a name='serializable-inline-classes'></a>[Serializable inline classes](inline-classes.md#serializable-inline-classes)
+* <a name='unsigned-types-support-json-only'></a>[Unsigned types support (JSON only)](inline-classes.md#unsigned-types-support-json-only)
+* <a name='using-inline-classes-in-your-custom-serializers'></a>[Using inline classes in your custom serializers](inline-classes.md#using-inline-classes-in-your-custom-serializers)
+<!--- END -->
diff --git a/docs/serializers.md b/docs/serializers.md
new file mode 100644
index 00000000..fc4e1a60
--- /dev/null
+++ b/docs/serializers.md
@@ -0,0 +1,1158 @@
+<!--- TEST_NAME SerializersTest -->
+
+# Serializers
+
+This is the third chapter of the [Kotlin Serialization Guide](serialization-guide.md).
+In this chapter we'll take a look at serializers in more detail, and we'll see how custom serializers can be written.
+
+**Table of contents**
+
+<!--- TOC -->
+
+* [Introduction to serializers](#introduction-to-serializers)
+ * [Plugin-generated serializer](#plugin-generated-serializer)
+ * [Plugin-generated generic serializer](#plugin-generated-generic-serializer)
+ * [Builtin primitive serializers](#builtin-primitive-serializers)
+ * [Constructing collection serializers](#constructing-collection-serializers)
+ * [Using top-level serializer function](#using-top-level-serializer-function)
+* [Custom serializers](#custom-serializers)
+ * [Primitive serializer](#primitive-serializer)
+ * [Delegating serializers](#delegating-serializers)
+ * [Composite serializer via surrogate](#composite-serializer-via-surrogate)
+ * [Hand-written composite serializer](#hand-written-composite-serializer)
+ * [Sequential decoding protocol (experimental)](#sequential-decoding-protocol-experimental)
+ * [Serializing 3rd party classes](#serializing-3rd-party-classes)
+ * [Passing a serializer manually](#passing-a-serializer-manually)
+ * [Specifying serializer on a property](#specifying-serializer-on-a-property)
+ * [Specifying serializers for a file](#specifying-serializers-for-a-file)
+ * [Custom serializers for a generic type](#custom-serializers-for-a-generic-type)
+ * [Format-specific serializers](#format-specific-serializers)
+* [Contextual serialization](#contextual-serialization)
+ * [Serializers module](#serializers-module)
+ * [Contextual serialization and generic classes](#contextual-serialization-and-generic-classes)
+* [Deriving external serializer for another Kotlin class (experimental)](#deriving-external-serializer-for-another-kotlin-class-experimental)
+ * [External serialization uses properties](#external-serialization-uses-properties)
+
+<!--- END -->
+
+## Introduction to serializers
+
+Formats, like JSON, control the _encoding_ of an object into specific output bytes, but how the object is decomposed
+into its constituent properties is controlled by a _serializer_. So far we've been using automatically-derived
+serializers by using the [`@Serializable`][Serializable] annotation as explained in
+the [Serializable classes](/docs/basic-serialization.md#serializable-classes) section, or using builtin serializers that were shown in
+the [Builtin classes](/docs/builtin-classes.md) section.
+
+As a motivating example, let us take the following `Color` class with an integer value storing its `rgb` bytes.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+-->
+
+```kotlin
+@Serializable
+class Color(val rgb: Int)
+
+fun main() {
+ val green = Color(0x00ff00)
+ println(Json.encodeToString(green))
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-01.kt).
+
+By default this class serializes its `rgb` property into JSON.
+
+```text
+{"rgb":65280}
+```
+
+<!--- TEST -->
+
+### Plugin-generated serializer
+
+Every class marked with the `@Serializable` annotation, like the `Color` class from the previous example,
+gets an instance of the [KSerializer] interface automatically generated by the Kotlin Serialization compiler plugin.
+We can retrieve this instance using the `.serializer()` function on the class's companion object.
+
+We can examine its [descriptor][KSerializer.descriptor] property that describes the structure of
+the serialized class. We'll learn more details about that in the upcoming sections.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+
+@Serializable
+@SerialName("Color")
+class Color(val rgb: Int)
+-->
+
+```kotlin
+fun main() {
+ val colorSerializer: KSerializer<Color> = Color.serializer()
+ println(colorSerializer.descriptor)
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-02.kt).
+
+```text
+Color(rgb: kotlin.Int)
+```
+
+<!--- TEST -->
+
+This serializer is automatically retrieved and used by the Kotlin Serialization framework when the `Color` class
+is itself serialized, or when it is used as a property of other classes.
+
+> You cannot define your own function `serializer()` on a companion object of a serializable class.
+
+### Plugin-generated generic serializer
+
+For generic classes, like the `Box` class shown in the [Generic classes](basic-serialization.md#generic-classes) section,
+the automatically generated `.serializer()` function accepts as many parameters as there are type parameters in the
+corresponding class. These parameters are of type [KSerializer], so the actual type argument's serializer has
+to be provided when constructing an instance of a serializer for a generic class.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+
+@Serializable
+@SerialName("Color")
+class Color(val rgb: Int)
+-->
+
+```kotlin
+@Serializable
+@SerialName("Box")
+class Box<T>(val contents: T)
+
+fun main() {
+ val boxedColorSerializer = Box.serializer(Color.serializer())
+ println(boxedColorSerializer.descriptor)
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-03.kt).
+
+As we can see, a serializer was instantiated to serialize a concrete `Box<Color>`.
+
+```text
+Box(contents: Color)
+```
+
+<!--- TEST -->
+
+### Builtin primitive serializers
+
+The serializers for the [primitive builtin classes](builtin-classes.md#primitives) can be retrieved
+using `.serializer()` extensions.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+-->
+
+```kotlin
+fun main() {
+ val intSerializer: KSerializer<Int> = Int.serializer()
+ println(intSerializer.descriptor)
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-04.kt).
+
+<!--- TEST
+PrimitiveDescriptor(kotlin.Int)
+-->
+
+### Constructing collection serializers
+
+[Builtin collection serializers](builtin-classes.md#lists), when needed, must be explicitly constructed
+using the corresponding functions [ListSerializer()], [SetSerializer()], [MapSerializer()], etc.
+These classes are generic, so to instantiate their serializer we must provide the serializers for the
+corresponding number of their type parameters.
+For example, we can produce a serializer for a `List<String>` in the following way.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+-->
+
+```kotlin
+fun main() {
+ val stringListSerializer: KSerializer<List<String>> = ListSerializer(String.serializer())
+ println(stringListSerializer.descriptor)
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-05.kt).
+
+<!--- TEST
+kotlin.collections.ArrayList(PrimitiveDescriptor(kotlin.String))
+-->
+
+### Using top-level serializer function
+
+When in doubt, you can always use the top-level generic `serializer<T>()`
+function to retrieve a serializer for an arbitrary Kotlin type in your source-code.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+-->
+
+```kotlin
+@Serializable
+@SerialName("Color")
+class Color(val rgb: Int)
+
+fun main() {
+ val stringToColorMapSerializer: KSerializer<Map<String, Color>> = serializer()
+ println(stringToColorMapSerializer.descriptor)
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-06.kt).
+
+<!--- TEST
+kotlin.collections.LinkedHashMap(PrimitiveDescriptor(kotlin.String), Color(rgb: kotlin.Int))
+-->
+
+## Custom serializers
+
+A plugin-generated serializer is convenient, but it may not produce the JSON we want
+for such a class as `Color`. Let's study alternatives.
+
+### Primitive serializer
+
+We want to serialize the `Color` class as a hex string with the green color represented as `"00ff00"`.
+To achieve this, we write an object that implements the [KSerializer] interface for the `Color` class.
+
+<!--- INCLUDE .*-serializer-.*
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+-->
+
+```kotlin
+object ColorAsStringSerializer : KSerializer<Color> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: Color) {
+ val string = value.rgb.toString(16).padStart(6, '0')
+ encoder.encodeString(string)
+ }
+
+ override fun deserialize(decoder: Decoder): Color {
+ val string = decoder.decodeString()
+ return Color(string.toInt(16))
+ }
+}
+```
+
+Serializer has three required pieces.
+
+* The [serialize][SerializationStrategy.serialize] function implements [SerializationStrategy].
+ It receives an instance of [Encoder] and a value to serialize.
+ It uses the `encodeXxx` functions of `Encoder` to represent a value as a sequence of primitives. There is an
+ `encodeXxx` for each primitive type supported by serialization.
+ In our example, [encodeString][Encoder.encodeString] is used.
+
+* The [deserialize][DeserializationStrategy.deserialize] function implements [DeserializationStrategy].
+ It receives an instance of [Decoder] and returns a
+ deserialized value. It uses the `decodeXxx` functions of `Decoder`, which mirror the corresponding functions of `Encoder`.
+ In our example [decodeString][Decoder.decodeString] is used.
+
+* The [descriptor][KSerializer.descriptor] property must faithfully explain what exactly the `encodeXxx` and `decodeXxx`
+ functions do so that a format implementation knows in advance what encoding/decoding methods they call.
+ Some formats might also use it to generate a schema for the serialized data. For primitive serialization,
+ the [PrimitiveSerialDescriptor][PrimitiveSerialDescriptor()] function must be used with a unique name of the
+ type that is being serialized.
+ [PrimitiveKind] describes the specific `encodeXxx`/`decodeXxx` method that is being used in the implementation.
+
+> When the `descriptor` does not correspond to the encoding/decoding methods, then the behavior of the resulting code
+> is unspecified, and may arbitrarily change in future updates.
+
+The next step is to bind a serializer to a class. This is done with the [`@Serializable`][Serializable] annotation by adding
+the [`with`][Serializable.with] property value.
+
+```kotlin
+@Serializable(with = ColorAsStringSerializer::class)
+class Color(val rgb: Int)
+```
+
+Now we can serialize the `Color` class as we did before.
+
+```kotlin
+fun main() {
+ val green = Color(0x00ff00)
+ println(Json.encodeToString(green))
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-07.kt).
+
+We get the serial representation as the hex string we wanted.
+
+```text
+"00ff00"
+```
+
+<!--- TEST -->
+
+Deserialization is also straightforward because we implemented the `deserialize` method.
+
+<!--- INCLUDE
+object ColorAsStringSerializer : KSerializer<Color> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: Color) {
+ val string = value.rgb.toString(16).padStart(6, '0')
+ encoder.encodeString(string)
+ }
+
+ override fun deserialize(decoder: Decoder): Color {
+ val string = decoder.decodeString()
+ return Color(string.toInt(16))
+ }
+}
+-->
+
+```kotlin
+@Serializable(with = ColorAsStringSerializer::class)
+class Color(val rgb: Int)
+
+fun main() {
+ val color = Json.decodeFromString<Color>("\"00ff00\"")
+ println(color.rgb) // prints 65280
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-08.kt).
+
+<!--- TEST
+65280
+-->
+
+It also works if we serialize or deserialize a different class with `Color` properties.
+
+<!--- INCLUDE
+object ColorAsStringSerializer : KSerializer<Color> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: Color) {
+ val string = value.rgb.toString(16).padStart(6, '0')
+ encoder.encodeString(string)
+ }
+
+ override fun deserialize(decoder: Decoder): Color {
+ val string = decoder.decodeString()
+ return Color(string.toInt(16))
+ }
+}
+-->
+
+```kotlin
+@Serializable(with = ColorAsStringSerializer::class)
+data class Color(val rgb: Int)
+
+@Serializable
+data class Settings(val background: Color, val foreground: Color)
+
+fun main() {
+ val data = Settings(Color(0xffffff), Color(0))
+ val string = Json.encodeToString(data)
+ println(string)
+ require(Json.decodeFromString<Settings>(string) == data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-09.kt).
+
+Both `Color` properties are serialized as strings.
+
+```text
+{"background":"ffffff","foreground":"000000"}
+```
+
+<!--- TEST -->
+
+### Delegating serializers
+
+In the previous example, we represented the `Color` class as a string.
+String is considered to be a primitive type, therefore we used `PrimitiveClassDescriptor` and specialized `encodeString` method.
+Now let's see what our actions would be if we have to serialize `Color` as another non-primitive type, let's say `IntArray`.
+
+An implementation of [KSerializer] for our original `Color` class is going to perform a conversion between
+`Color` and `IntArray`, but delegate the actual serialization logic to the `IntArraySerializer`
+using [encodeSerializableValue][Encoder.encodeSerializableValue] and
+[decodeSerializableValue][Decoder.decodeSerializableValue].
+
+```kotlin
+import kotlinx.serialization.builtins.IntArraySerializer
+
+class ColorIntArraySerializer : KSerializer<Color> {
+ private val delegateSerializer = IntArraySerializer()
+ override val descriptor = SerialDescriptor("Color", delegateSerializer.descriptor)
+
+ override fun serialize(encoder: Encoder, value: Color) {
+ val data = intArrayOf(
+ (value.rgb shr 16) and 0xFF,
+ (value.rgb shr 8) and 0xFF,
+ value.rgb and 0xFF
+ )
+ encoder.encodeSerializableValue(delegateSerializer, data)
+ }
+
+ override fun deserialize(decoder: Decoder): Color {
+ val array = decoder.decodeSerializableValue(delegateSerializer)
+ return Color((array[0] shl 16) or (array[1] shl 8) or array[2])
+ }
+}
+```
+
+Note that we can't use default `Color.serializer().descriptor` here because formats that rely
+on the schema may think that we would call `encodeInt` instead of `encodeSerializableValue`.
+Neither we can use `IntArraySerializer().descriptor` directly — otherwise, formats that handle int arrays specially
+can't tell if `value` is really a `IntArray` or a `Color`. Don't worry, this optimization would still kick in
+when serializing actual underlying int array.
+
+> Example of how format can treat arrays specially is shown in the [formats guide](formats.md#format-specific-types).
+
+Now we can use the serializer:
+
+```kotlin
+@Serializable(with = ColorIntArraySerializer::class)
+class Color(val rgb: Int)
+
+fun main() {
+ val green = Color(0x00ff00)
+ println(Json.encodeToString(green))
+}
+```
+
+As you can see, such array representation is not very useful in JSON,
+but may save some space when used with a `ByteArray` and a binary format.
+
+> You can get the full code [here](../guide/example/example-serializer-10.kt).
+
+```text
+[0,255,0]
+```
+
+<!--- TEST -->
+
+
+### Composite serializer via surrogate
+
+Now our challenge is to get `Color` serialized so that it is represented in JSON as if it is a class
+with three properties&mdash;`r`, `g`, and `b`&mdash;so that JSON encodes it as an object.
+The easiest way to achieve this is to define a _surrogate_ class mimicking the serialized form of `Color` that
+we are going to use for its serialization. We also set the [SerialName] of this surrogate class to `Color`. Then if
+any format uses this name the surrogate looks like it is a `Color` class.
+The surrogate class can be `private`, and can enforce all the constraints on the serial representation
+of the class in its `init` block.
+
+```kotlin
+@Serializable
+@SerialName("Color")
+private class ColorSurrogate(val r: Int, val g: Int, val b: Int) {
+ init {
+ require(r in 0..255 && g in 0..255 && b in 0..255)
+ }
+}
+```
+
+> An example of where the class name is used is shown in
+> the [Custom subclass serial name](polymorphism.md#custom-subclass-serial-name) section in the chapter on polymorphism.
+
+Now we can use the `ColorSurrogate.serializer()` function to retrieve a plugin-generated serializer for the
+surrogate class.
+
+We can use the same approach as in [delegating serializer](#delegating-serializers), but this time,
+we are fully reusing an automatically
+generated [SerialDescriptor] for the surrogate because it should be indistinguishable from the original.
+
+```kotlin
+object ColorSerializer : KSerializer<Color> {
+ override val descriptor: SerialDescriptor = ColorSurrogate.serializer().descriptor
+
+ override fun serialize(encoder: Encoder, value: Color) {
+ val surrogate = ColorSurrogate((value.rgb shr 16) and 0xff, (value.rgb shr 8) and 0xff, value.rgb and 0xff)
+ encoder.encodeSerializableValue(ColorSurrogate.serializer(), surrogate)
+ }
+
+ override fun deserialize(decoder: Decoder): Color {
+ val surrogate = decoder.decodeSerializableValue(ColorSurrogate.serializer())
+ return Color((surrogate.r shl 16) or (surrogate.g shl 8) or surrogate.b)
+ }
+}
+```
+
+We bind the `ColorSerializer` serializer to the `Color` class.
+
+```kotlin
+@Serializable(with = ColorSerializer::class)
+class Color(val rgb: Int)
+```
+
+Now we can enjoy the result of serialization for the `Color` class.
+
+<!--- INCLUDE
+fun main() {
+ val green = Color(0x00ff00)
+ println(Json.encodeToString(green))
+}
+-->
+
+> You can get the full code [here](../guide/example/example-serializer-11.kt).
+
+```text
+{"r":0,"g":255,"b":0}
+```
+
+<!--- TEST -->
+
+### Hand-written composite serializer
+
+There are some cases where a surrogate solution does not fit. Perhaps we want to avoid the performance
+implications of additional allocation, or we want a configurable/dynamic set of properties for the
+resulting serial representation. In these cases we need to manually write a class
+serializer which mimics the behaviour of a generated serializer.
+
+```kotlin
+object ColorAsObjectSerializer : KSerializer<Color> {
+```
+
+Let's introduce it piece by piece. First, a descriptor is defined using the [buildClassSerialDescriptor] builder.
+The [element][ClassSerialDescriptorBuilder.element] function in the builder DSL automatically fetches serializers
+for the corresponding fields by their type. The order of elements is important. They are indexed starting from zero.
+
+```kotlin
+ override val descriptor: SerialDescriptor =
+ buildClassSerialDescriptor("Color") {
+ element<Int>("r")
+ element<Int>("g")
+ element<Int>("b")
+ }
+```
+
+> The "element" is a generic term here. What is an element of a descriptor depends on its [SerialKind].
+> Elements of a class descriptor are its properties, elements of a enum descriptor are its cases, etc.
+
+Then we write the `serialize` function using the [encodeStructure] DSL that provides access to
+the [CompositeEncoder] in its block. The difference between [Encoder] and [CompositeEncoder] is the latter
+has `encodeXxxElement` functions that correspond to the `encodeXxx` functions of the former. They must be called
+in the same order as in the descriptor.
+
+```kotlin
+ override fun serialize(encoder: Encoder, value: Color) =
+ encoder.encodeStructure(descriptor) {
+ encodeIntElement(descriptor, 0, (value.rgb shr 16) and 0xff)
+ encodeIntElement(descriptor, 1, (value.rgb shr 8) and 0xff)
+ encodeIntElement(descriptor, 2, value.rgb and 0xff)
+ }
+```
+
+The most complex piece of code is the `deserialize` function. It must support formats, like JSON, that
+can decode properties in an arbitrary order. It starts with the call to [decodeStructure] to
+get access to a [CompositeDecoder]. Inside it we write a loop that repeatedly calls
+[decodeElementIndex][CompositeDecoder.decodeElementIndex] to decode the index of the next element, then we decode the corresponding
+element using [decodeIntElement][CompositeDecoder.decodeIntElement] in our example, and finally we terminate the loop when
+`CompositeDecoder.DECODE_DONE` is encountered.
+
+```kotlin
+ override fun deserialize(decoder: Decoder): Color =
+ decoder.decodeStructure(descriptor) {
+ var r = -1
+ var g = -1
+ var b = -1
+ while (true) {
+ when (val index = decodeElementIndex(descriptor)) {
+ 0 -> r = decodeIntElement(descriptor, 0)
+ 1 -> g = decodeIntElement(descriptor, 1)
+ 2 -> b = decodeIntElement(descriptor, 2)
+ CompositeDecoder.DECODE_DONE -> break
+ else -> error("Unexpected index: $index")
+ }
+ }
+ require(r in 0..255 && g in 0..255 && b in 0..255)
+ Color((r shl 16) or (g shl 8) or b)
+ }
+```
+
+<!--- INCLUDE
+}
+-->
+
+Now we bind the resulting serializer to the `Color` class and test its serialization/deserialization.
+
+```kotlin
+@Serializable(with = ColorAsObjectSerializer::class)
+data class Color(val rgb: Int)
+
+fun main() {
+ val color = Color(0x00ff00)
+ val string = Json.encodeToString(color)
+ println(string)
+ require(Json.decodeFromString<Color>(string) == color)
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-12.kt).
+
+As before, we got the `Color` class represented as a JSON object with three keys:
+
+```text
+{"r":0,"g":255,"b":0}
+```
+
+<!--- TEST -->
+
+### Sequential decoding protocol (experimental)
+
+The implementation of the `deserialize` function from the previous section works with any format. However,
+some formats either always store all the complex data in order, or only do so sometimes (JSON always stores
+collections in order). With these formats the complex protocol of calling `decodeElementIndex` in the loop is
+not needed, and a faster implementation can be used if the [CompositeDecoder.decodeSequentially] function returns `true`.
+The plugin-generated serializers are actually conceptually similar to the below code.
+
+<!--- INCLUDE
+object ColorAsObjectSerializer : KSerializer<Color> {
+
+ override val descriptor: SerialDescriptor =
+ buildClassSerialDescriptor("Color") {
+ element<Int>("r")
+ element<Int>("g")
+ element<Int>("b")
+ }
+
+ override fun serialize(encoder: Encoder, value: Color) =
+ encoder.encodeStructure(descriptor) {
+ encodeIntElement(descriptor, 0, (value.rgb shr 16) and 0xff)
+ encodeIntElement(descriptor, 1, (value.rgb shr 8) and 0xff)
+ encodeIntElement(descriptor, 2, value.rgb and 0xff)
+ }
+-->
+
+```kotlin
+ override fun deserialize(decoder: Decoder): Color =
+ decoder.decodeStructure(descriptor) {
+ var r = -1
+ var g = -1
+ var b = -1
+ if (decodeSequentially()) { // sequential decoding protocol
+ r = decodeIntElement(descriptor, 0)
+ g = decodeIntElement(descriptor, 1)
+ b = decodeIntElement(descriptor, 2)
+ } else while (true) {
+ when (val index = decodeElementIndex(descriptor)) {
+ 0 -> r = decodeIntElement(descriptor, 0)
+ 1 -> g = decodeIntElement(descriptor, 1)
+ 2 -> b = decodeIntElement(descriptor, 2)
+ CompositeDecoder.DECODE_DONE -> break
+ else -> error("Unexpected index: $index")
+ }
+ }
+ require(r in 0..255 && g in 0..255 && b in 0..255)
+ Color((r shl 16) or (g shl 8) or b)
+ }
+```
+
+<!--- INCLUDE
+}
+
+@Serializable(with = ColorAsObjectSerializer::class)
+data class Color(val rgb: Int)
+
+fun main() {
+ val color = Color(0x00ff00)
+ val string = Json.encodeToString(color)
+ println(string)
+ require(Json.decodeFromString<Color>(string) == color)
+}
+-->
+
+> You can get the full code [here](../guide/example/example-serializer-13.kt).
+
+<!--- TEST
+{"r":0,"g":255,"b":0}
+-->
+
+### Serializing 3rd party classes
+
+Sometimes an application has to work with an external type that is not serializable.
+Let us use [java.util.Date] as an example. As before, we start by writing an implementation of [KSerializer]
+for the class. Our goal is to get a `Date` serialized as a long number of milliseconds following the
+approach from the [Primitive serializer](#primitive-serializer) section.
+
+> In the following sections any kind of `Date` serializer would work. For example, if we want `Date` to be serialized
+> as an object, we would use an approach from
+> the [Composite serializer via surrogate](#composite-serializer-via-surrogate) section.
+> See also [Deriving external serializer for another Kotlin class (experimental)](#deriving-external-serializer-for-another-kotlin-class-experimental)
+> when you need to serialize a 3rd-party Kotlin class that could have been serializable, but is not.
+
+<!--- INCLUDE
+import java.util.Date
+import java.text.SimpleDateFormat
+-->
+
+```kotlin
+object DateAsLongSerializer : KSerializer<Date> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
+ override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
+ override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
+}
+```
+
+We cannot bind the `DateAsLongSerializer` serializer to the `Date` class with the [`@Serializable`][Serializable] annotation
+because we don't control the `Date` source code. There are several ways to work around that.
+
+### Passing a serializer manually
+
+All `encodeToXxx` and `decodeFromXxx` functions have an overload with the first serializer parameter.
+When a non-serializable class, like `Date`, is the top-level class being serialized we can use those.
+
+```kotlin
+fun main() {
+ val kotlin10ReleaseDate = SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00")
+ println(Json.encodeToString(DateAsLongSerializer, kotlin10ReleaseDate))
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-14.kt).
+
+```text
+1455494400000
+```
+
+<!--- TEST -->
+
+### Specifying serializer on a property
+
+When a property of a non-serializable class, like `Date`, is serialized as part of a serializable class we must supply
+its serializer or the code will not compile. This is accomplished using the [`@Serializable`][Serializable] annotation on the property.
+
+<!--- INCLUDE
+import java.util.Date
+import java.text.SimpleDateFormat
+
+object DateAsLongSerializer : KSerializer<Date> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
+ override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
+ override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
+}
+-->
+
+```kotlin
+@Serializable
+class ProgrammingLanguage(
+ val name: String,
+ @Serializable(with = DateAsLongSerializer::class)
+ val stableReleaseDate: Date
+)
+
+fun main() {
+ val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
+ println(Json.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-15.kt).
+
+The `stableReleaseDate` property is serialized with the serialization strategy that we specified for it:
+
+```text
+{"name":"Kotlin","stableReleaseDate":1455494400000}
+```
+
+<!--- TEST -->
+
+### Specifying serializers for a file
+
+A serializer for a specific type, like `Date`, can be specified for a whole source code file with the file-level
+[UseSerializers] annotation at the beginning of the file.
+
+```kotlin
+@file:UseSerializers(DateAsLongSerializer::class)
+```
+
+<!--- PREFIX -->
+
+<!--- INCLUDE
+import java.util.Date
+import java.text.SimpleDateFormat
+
+object DateAsLongSerializer : KSerializer<Date> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
+ override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
+ override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
+}
+-->
+
+Now a `Date` property can be used in a serializable class without additional annotations.
+
+```kotlin
+@Serializable
+class ProgrammingLanguage(val name: String, val stableReleaseDate: Date)
+
+fun main() {
+ val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
+ println(Json.encodeToString(data))
+}
+```
+> You can get the full code [here](../guide/example/example-serializer-16.kt).
+
+```text
+{"name":"Kotlin","stableReleaseDate":1455494400000}
+```
+
+<!--- TEST -->
+
+### Custom serializers for a generic type
+
+Let us take a look at the following example of the generic `Box<T>` class.
+It is marked with `@Serializable(with = BoxSerializer::class)` as we plan to have a custom serialization
+strategy for it.
+
+```kotlin
+@Serializable(with = BoxSerializer::class)
+data class Box<T>(val contents: T)
+```
+
+An implementation of [KSerializer] for a regular type is written as an `object`, as we saw in this chapter's
+examples for the `Color` type. A generic class serializer is instantiated with serializers
+for its generic parameters. We saw this in the [Plugin-generated generic serializer](#plugin-generated-generic-serializer) section.
+A custom serializer for a generic class must be a `class` with a constructor that accepts as many [KSerializer]
+parameters as the type has generic parameters. Let us write a `Box<T>` serializer that erases itself during
+serialization, delegating everything to the underlying serializer of its `data` property.
+
+```kotlin
+class BoxSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Box<T>> {
+ override val descriptor: SerialDescriptor = dataSerializer.descriptor
+ override fun serialize(encoder: Encoder, value: Box<T>) = dataSerializer.serialize(encoder, value.contents)
+ override fun deserialize(decoder: Decoder) = Box(dataSerializer.deserialize(decoder))
+}
+```
+
+Now we can serialize and deserialize `Box<Project>`.
+
+```kotlin
+@Serializable
+data class Project(val name: String)
+
+fun main() {
+ val box = Box(Project("kotlinx.serialization"))
+ val string = Json.encodeToString(box)
+ println(string)
+ println(Json.decodeFromString<Box<Project>>(string))
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-17.kt).
+
+The resulting JSON looks like the `Project` class was serialized directly.
+
+```text
+{"name":"kotlinx.serialization"}
+Box(contents=Project(name=kotlinx.serialization))
+```
+
+<!--- TEST -->
+
+### Format-specific serializers
+
+The above custom serializers worked in the same way for every format. However, there might be format-specific
+features that a serializer implementation would like to take advantage of.
+
+* The [Json transformations](json.md#json-transformations) section of the [Json](json.md) chapter provides examples
+ of serializers that utilize JSON-specific features.
+
+* A format implementation can have a format-specific representation for a type as explained
+ in the [Format-specific types](formats.md#format-specific-types) section of
+ the [Alternative and custom formats (experimental)](formats.md) chapter.
+
+This chapter proceeds with a generic approach to tweaking the serialization strategy based on the context.
+
+## Contextual serialization
+
+All the previous approaches to specifying custom serialization strategies were _static_, that is
+fully defined at compile-time. The exception was the [Passing a serializer manually](#passing-a-serializer-manually)
+approach, but it worked only on a top-level object. You might need to change the serialization
+strategy for objects deep in the serialized object tree at run-time, with the strategy being selected in a context-dependent way.
+For example, you might want to represent `java.util.Date` in JSON format as an ISO 8601 string or as a long integer
+depending on a version of a protocol you are serializing data for. This is called _contextual_ serialization, and it
+is supported by a built-in [ContextualSerializer] class. Usually we don't have to use this serializer class explicitly&mdash;there
+is the [Contextual] annotation providing a shortcut to
+the `@Serializable(with = ContextualSerializer::class)` annotation,
+or the [UseContextualSerialization] annotation can be used at the file-level just like
+the [UseSerializers] annotation. Let's see an example utilizing the former.
+
+<!--- INCLUDE
+import java.util.Date
+import java.text.SimpleDateFormat
+-->
+
+```kotlin
+@Serializable
+class ProgrammingLanguage(
+ val name: String,
+ @Contextual
+ val stableReleaseDate: Date
+)
+```
+
+<!--- INCLUDE
+
+fun main() {
+ val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
+ println(Json.encodeToString(data))
+}
+-->
+
+To actually serialize this class we must provide the corresponding context when calling the `encodeToXxx`/`decodeFromXxx`
+functions. Without it we'll get a "Serializer for class 'Date' is not found" exception.
+
+> See [here](../guide/example/example-serializer-18.kt) for an example that produces that exception.
+
+<!--- TEST LINES_START
+Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Date' is not found.
+Mark the class as @Serializable or provide the serializer explicitly.
+-->
+
+<!--- INCLUDE
+import kotlinx.serialization.modules.*
+import java.util.Date
+import java.text.SimpleDateFormat
+
+object DateAsLongSerializer : KSerializer<Date> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
+ override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
+ override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
+}
+
+@Serializable
+class ProgrammingLanguage(
+ val name: String,
+ @Contextual
+ val stableReleaseDate: Date
+)
+-->
+
+### Serializers module
+
+To provide a context, we define a [SerializersModule] instance that describes which serializers shall be used
+at run-time to serialize which contextually-serializable classes. This is done using the
+[SerializersModule {}][SerializersModule()] builder function, which provides the [SerializersModuleBuilder] DSL to
+register serializers. In the below example we use the [contextual][_contextual] function with the serializer. The corresponding
+class this serializer is defined for is fetched automatically via the `reified` type parameter.
+
+```kotlin
+private val module = SerializersModule {
+ contextual(DateAsLongSerializer)
+}
+```
+
+Next we create an instance of the [Json] format with this module using the
+[Json {}][Json()] builder function and the [serializersModule][JsonBuilder.serializersModule] property.
+
+> Details on custom JSON configurations can be found in
+> the [JSON configuration](json.md#json-configuration) section.
+
+```kotlin
+val format = Json { serializersModule = module }
+```
+
+Now we can serialize our data with this `format`.
+
+```kotlin
+fun main() {
+ val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
+ println(format.encodeToString(data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-19.kt).
+```text
+{"name":"Kotlin","stableReleaseDate":1455494400000}
+```
+
+<!--- TEST -->
+
+### Contextual serialization and generic classes
+
+In the previous section we saw that we can register serializer instance in the module for a class we want to serialize contextually.
+We also know that [serializers for generic classes have constructor parameters](#custom-serializers-for-a-generic-type) — type arguments serializers.
+It means that we can't use one serializer instance for a class if this class is generic:
+
+```kotlin
+val incorrectModule = SerializersModule {
+ // Can serialize only Box<Int>, but not Box<String> or others
+ contextual(BoxSerializer(Int.serializer()))
+}
+```
+
+For cases when one want to serialize contextually a generic class, it is possible to register provider in the module:
+
+```kotlin
+val correctModule = SerializersModule {
+ // args[0] contains Int.serializer() or String.serializer(), depending on the usage
+ contextual(Box::class) { args -> BoxSerializer(args[0]) }
+}
+```
+
+<!--- CLEAR -->
+
+> Additional details on serialization modules are given in
+> the [Merging library serializers modules](polymorphism.md#merging-library-serializers-modules) section of
+> the [Polymorphism](polymorphism.md) chapter.
+
+## Deriving external serializer for another Kotlin class (experimental)
+
+If a 3rd-party class to be serialized is a Kotlin class with a properties-only primary constructor, a kind of
+class which could have been made `@Serializable`, then you can generate an _external_ serializer for it
+using the [Serializer] annotation on an object with the [`forClass`][Serializer.forClass] property.
+
+```kotlin
+// NOT @Serializable
+class Project(val name: String, val language: String)
+
+@Serializer(forClass = Project::class)
+object ProjectSerializer
+```
+
+You must bind this serializer to a class using one of the approaches explained in this chapter. We'll
+follow the [Passing a serializer manually](#passing-a-serializer-manually) approach for this example.
+
+```kotlin
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(Json.encodeToString(ProjectSerializer, data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-20.kt).
+
+This gets all the `Project` properties serialized:
+
+```text
+{"name":"kotlinx.serialization","language":"Kotlin"}
+```
+
+<!--- TEST -->
+
+### External serialization uses properties
+
+As we saw earlier, the regular `@Serializable` annotation creates a serializer so that
+[Backing fields are serialized](basic-serialization.md#backing-fields-are-serialized). _External_ serialization using
+`Serializer(forClass = ...)` has no access to backing fields and works differently.
+It serializes only _accessible_ properties that have setters or are part of the primary constructor.
+The following example shows this.
+
+```kotlin
+// NOT @Serializable, will use external serializer
+class Project(
+ // val in a primary constructor -- serialized
+ val name: String
+) {
+ var stars: Int = 0 // property with getter & setter -- serialized
+
+ val path: String // getter only -- not serialized
+ get() = "kotlin/$name"
+
+ private var locked: Boolean = false // private, not accessible -- not serialized
+}
+
+@Serializer(forClass = Project::class)
+object ProjectSerializer
+
+fun main() {
+ val data = Project("kotlinx.serialization").apply { stars = 9000 }
+ println(Json.encodeToString(ProjectSerializer, data))
+}
+```
+
+> You can get the full code [here](../guide/example/example-serializer-21.kt).
+
+The output is shown below.
+
+```text
+{"name":"kotlinx.serialization","stars":9000}
+```
+
+<!--- TEST -->
+
+---
+
+The next chapter covers [Polymorphism](polymorphism.md).
+
+<!-- Java references -->
+[java.util.Date]: https://docs.oracle.com/javase/8/docs/api/java/util/Date.html
+
+<!--- MODULE /kotlinx-serialization-core -->
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
+
+[Serializable]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html
+[KSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html
+[KSerializer.descriptor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/descriptor.html
+[SerializationStrategy.serialize]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serialization-strategy/serialize.html
+[SerializationStrategy]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serialization-strategy/index.html
+[DeserializationStrategy.deserialize]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-deserialization-strategy/deserialize.html
+[DeserializationStrategy]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-deserialization-strategy/index.html
+[Serializable.with]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/with.html
+[SerialName]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html
+[UseSerializers]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-use-serializers/index.html
+[ContextualSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-contextual-serializer/index.html
+[Contextual]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-contextual/index.html
+[UseContextualSerialization]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-use-contextual-serialization/index.html
+[Serializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializer/index.html
+[Serializer.forClass]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializer/for-class.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.builtins -->
+
+[ListSerializer()]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.builtins/-list-serializer.html
+[SetSerializer()]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.builtins/-set-serializer.html
+[MapSerializer()]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.builtins/-map-serializer.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding -->
+
+[Encoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html
+[Encoder.encodeString]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-string.html
+[Decoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html
+[Decoder.decodeString]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/decode-string.html
+[Encoder.encodeSerializableValue]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-serializable-value.html
+[Decoder.decodeSerializableValue]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/decode-serializable-value.html
+[encodeStructure]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/encode-structure.html
+[CompositeEncoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-encoder/index.html
+[decodeStructure]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/decode-structure.html
+[CompositeDecoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/index.html
+[CompositeDecoder.decodeElementIndex]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-element-index.html
+[CompositeDecoder.decodeIntElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-int-element.html
+[CompositeDecoder.decodeSequentially]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-sequentially.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.descriptors -->
+
+[PrimitiveSerialDescriptor()]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-primitive-serial-descriptor.html
+[PrimitiveKind]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-primitive-kind/index.html
+[SerialDescriptor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-descriptor/index.html
+[buildClassSerialDescriptor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/build-class-serial-descriptor.html
+[ClassSerialDescriptorBuilder.element]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/element.html
+[SerialKind]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-kind/index.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.modules -->
+
+[SerializersModule]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module/index.html
+[SerializersModule()]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module.html
+[SerializersModuleBuilder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module-builder/index.html
+[_contextual]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/contextual.html
+
+<!--- MODULE /kotlinx-serialization-json -->
+<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json -->
+
+[Json]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html
+[Json()]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json.html
+[JsonBuilder.serializersModule]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/serializers-module.html
+
+<!--- END -->
+
diff --git a/dokka/moduledoc.md b/dokka/moduledoc.md
new file mode 100644
index 00000000..e3c12610
--- /dev/null
+++ b/dokka/moduledoc.md
@@ -0,0 +1,52 @@
+# Module kotlinx-serialization-core
+Core serialization API and serializers for standard library classes, and ready to use JSON
+format implementation.
+
+# Module kotlinx-serialization-json
+Stable and ready to use JSON format implementation, `JsonElement` API to operate with JSON trees and JSON-specific serializers.
+
+# Module kotlinx-serialization-cbor
+Concise Binary Object Representation (CBOR) format implementation, as per [RFC 7049](https://tools.ietf.org/html/rfc7049).
+
+# Module kotlinx-serialization-hocon
+Allows deserialization of `Config` object from popular [lightbend/config](https://github.com/lightbend/config) library
+into Kotlin objects.
+You can learn about "Human-Optimized Config Object Notation" or HOCON from library's [readme](https://github.com/lightbend/config#using-hocon-the-json-superset).
+
+# Module kotlinx-serialization-properties
+Allows converting arbitrary hierarchy of Kotlin classes to a flat key-value structure à la Java Properties.
+
+# Module kotlinx-serialization-protobuf
+Protocol buffers serialization format implementation, mostly compliant to [proto2](https://developers.google.com/protocol-buffers/docs/proto) specification.
+
+# Package kotlinx.serialization
+Basic core concepts and annotations that set up serialization process.
+
+# Package kotlinx.serialization.builtins
+Serializers for standard Kotlin types, like Int, String, List, etc.
+
+# Package kotlinx.serialization.descriptors
+Basic concepts of serial description to programmatically describe the serial form for serializers
+in an introspectable manner.
+
+# Package kotlinx.serialization.encoding
+Basic concepts of encoding and decoding of serialized data.
+
+# Package kotlinx.serialization.modules
+Classes that provides runtime mechanisms for resolving serializers, typically used during polymorphic serialization.
+
+# Package kotlinx.serialization.hocon
+HOCON serialization format implementation for converting Kotlin classes from and to [Lightbend config](https://github.com/lightbend/config).
+
+# Package kotlinx.serialization.json
+JSON serialization format implementation, JSON tree data structures with builders for them,
+and JSON-specific serializers.
+
+# Package kotlinx.serialization.protobuf
+Protocol buffers serialization format implementation, mostly compliant to [proto2](https://developers.google.com/protocol-buffers/docs/proto) specification.
+
+# Package kotlinx.serialization.properties
+Properties serialization format implementation that represents the input data as a plain map of properties.
+
+# Package kotlinx.serialization.cbor
+Concise Binary Object Representation (CBOR) format implementation, as per [RFC 7049](https://tools.ietf.org/html/rfc7049).
diff --git a/formats/README.md b/formats/README.md
new file mode 100644
index 00000000..eb29d61f
--- /dev/null
+++ b/formats/README.md
@@ -0,0 +1,145 @@
+# Serialization formats
+
+This area of repository contains different libraries with various add-on formats which
+were not included in the core library.
+
+For convenience, they have same `groupId`, versioning and release cycle as core library.
+
+## JSON
+
+* Artifact id: `kotlinx-serialization-json`
+* Platform: all supported platforms
+* Status: stable
+
+## HOCON
+
+* Artifact id: `kotlinx-serialization-hocon`
+* Platform: JVM only
+* Status: experimental
+
+Allows deserialization of `Config` object from popular [lightbend/config](https://github.com/lightbend/config) library
+into Kotlin objects.
+You can learn about "Human-Optimized Config Object Notation" or HOCON from library's [readme](https://github.com/lightbend/config#using-hocon-the-json-superset).
+
+## ProtoBuf
+
+* Artifact id: `kotlinx-serialization-protobuf`
+* Platform: all supported platforms
+* Status: experimental
+
+## CBOR
+
+* Artifact id: `kotlinx-serialization-cbor`
+* Platform: all supported platforms
+* Status: experimental
+
+## Properties
+
+* Artifact id: `kotlinx-serialization-properties`
+* Platform: all supported platforms
+* Status: experimental
+
+Allows converting arbitrary hierarchy of Kotlin classes to a flat key-value structure à la Java Properties.
+
+## Other community-supported formats
+
+### Avro
+
+* GitHub repo: [sksamuel/avro4k](https://github.com/sksamuel/avro4k)
+* Artifact ID: `com.sksamuel.avro4k:avro4k`
+* Platform: JVM only
+
+This library allows serialization and deserialization of objects to and from [Avro](https://avro.apache.org). It will read and write from Avro binary or json streams or generate Avro Generic Records directly. It will also generate Avro schemas from data classes. The library allows for easy extension and overrides for custom schema formats, compatiblity with schemas defined outside out of the JVM and for types not supported out of the box.
+
+### Bson
+
+* GitHub repo: [jershell/kbson](https://github.com/jershell/kbson)
+* Artifact ID: `com.github.jershell:kbson`
+* Platform: JVM only
+
+Allows serialization and deserialization of objects to and from [BSON](https://docs.mongodb.com/manual/reference/bson-types/).
+
+### Ktoml
+* GitHub repo: [akuleshov7/ktoml](https://github.com/akuleshov7/ktoml)
+* Artifact ID: `com.akuleshov7:ktoml-core`
+* Platforms: multiplatform, all Kotlin supported platforms
+
+Fully Native and Multiplatform Kotlin serialization library for serialization/deserialization of TOML format.
+This library contains no Java code and no Java dependencies and it implements multiplatform parser, decoder and encoder of TOML.
+
+### Minecraft NBT (Multiplatform)
+
+* GitHub repo: [BenWoodworth/knbt](https://github.com/BenWoodworth/knbt)
+* Artifact ID: `net.benwoodworth.knbt:knbt`
+* Platform: all supported platforms
+
+Implements the [NBT format](https://minecraft.fandom.com/wiki/NBT_format) for kotlinx.serialization, and
+provides a type-safe DSL for constructing NBT tags.
+
+### MsgPack (Multiplatform)
+
+* GitHub repo: [esensar/kotlinx-serialization-msgpack](https://github.com/esensar/kotlinx-serialization-msgpack)
+* Artifact ID: `com.ensarsarajcic.kotlinx:serialization-msgpack`
+* Platform: all supported platforms
+
+Allows serialization and deserialization of objects to and from [MsgPack](https://msgpack.org/).
+
+### SharedPreferences
+
+* GitHub repo: [EdwarDDay/serialization.kprefs](https://github.com/EdwarDDay/serialization.kprefs)
+* Artifact ID: `net.edwardday.serialization:kprefs`
+* Platform: Android only
+
+This library allows serialization and deserialization of objects into and from Android
+[SharedPreferences](https://developer.android.com/reference/android/content/SharedPreferences).
+
+### XML
+* GitHub repo: [pdvrieze/xmlutil](https://github.com/pdvrieze/xmlutil)
+* Artifact ID: `io.github.pdvrieze.xmlutil:serialization`
+* Platform: all supported platforms
+
+This library allows for reading and writing of XML documents with the serialization library.
+It is multiplatform, providing both a shared parser/writer for xml as well as platform-specific
+parsers where available. The library is designed to handle existing xml formats that use features that would
+not be available in other formats such as JSON.
+
+### YAML
+
+* GitHub repo: [charleskorn/kaml](https://github.com/charleskorn/kaml)
+* Artifact ID: `com.charleskorn.kaml:kaml`
+* Platform: JVM only
+
+Allows serialization and deserialization of objects to and from [YAML](http://yaml.org).
+
+### YAML (Multiplatform)
+
+* GitHub repo: [him188/yamlkt](https://github.com/him188/yamlkt)
+* Artifact ID: `net.mamoe.yamlkt:yamlkt`
+* Platform: all supported platforms
+
+Allows serialization and deserialization of objects to and from [YAML](http://yaml.org).
+Basic serial operations have been implemented, but some features such as compound keys and polymorphism are still work in progress.
+
+### CBOR
+
+* GitHub repo: [L-Briand/obor](https://github.com/L-Briand/obor)
+* Artifact ID: `net.orandja.obor:obor`
+* Platform: JVM, Android
+
+Allow serialization and deserialization of objects to and from [CBOR](https://cbor.io/). This codec can be used to read and write from Java InputStream and OutputStream.
+
+### Amazon Ion (binary only)
+
+* GitHub repo: [dimitark/kotlinx-serialization-ion](https://github.com/dimitark/kotlinx-serialization-ion)
+* Artifact ID: `com.github.dimitark:kotlinx-serialization-ion`
+* Platform: JVM
+
+Allow serialization and deserialization of objects to and from [Amazon Ion](https://amzn.github.io/ion-docs/). It stores the data in a flat binary format. Upon destialization, it retains the references between the objects.
+
+### android.os.Bundle
+
+* GitHub repo: [AhmedMourad0/bundlizer](https://github.com/AhmedMourad0/bundlizer)
+* Artifact ID: `dev.ahmedmourad.bundlizer:bundlizer-core`
+* Platform: Android
+
+Allow serialization and deserialization of objects to and from [android.os.Bundle](https://developer.android.com/reference/android/os/Bundle).
diff --git a/formats/cbor/api/kotlinx-serialization-cbor.api b/formats/cbor/api/kotlinx-serialization-cbor.api
new file mode 100644
index 00000000..825c55f7
--- /dev/null
+++ b/formats/cbor/api/kotlinx-serialization-cbor.api
@@ -0,0 +1,32 @@
+public abstract interface annotation class kotlinx/serialization/cbor/ByteString : java/lang/annotation/Annotation {
+}
+
+public final class kotlinx/serialization/cbor/ByteString$Impl : kotlinx/serialization/cbor/ByteString {
+ public fun <init> ()V
+}
+
+public abstract class kotlinx/serialization/cbor/Cbor : kotlinx/serialization/BinaryFormat {
+ public static final field Default Lkotlinx/serialization/cbor/Cbor$Default;
+ public synthetic fun <init> (ZZLkotlinx/serialization/modules/SerializersModule;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun decodeFromByteArray (Lkotlinx/serialization/DeserializationStrategy;[B)Ljava/lang/Object;
+ public fun encodeToByteArray (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)[B
+ public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public final class kotlinx/serialization/cbor/Cbor$Default : kotlinx/serialization/cbor/Cbor {
+}
+
+public final class kotlinx/serialization/cbor/CborBuilder {
+ public final fun getEncodeDefaults ()Z
+ public final fun getIgnoreUnknownKeys ()Z
+ public final fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+ public final fun setEncodeDefaults (Z)V
+ public final fun setIgnoreUnknownKeys (Z)V
+ public final fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V
+}
+
+public final class kotlinx/serialization/cbor/CborKt {
+ public static final fun Cbor (Lkotlinx/serialization/cbor/Cbor;Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/cbor/Cbor;
+ public static synthetic fun Cbor$default (Lkotlinx/serialization/cbor/Cbor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/serialization/cbor/Cbor;
+}
+
diff --git a/formats/cbor/build.gradle b/formats/cbor/build.gradle
new file mode 100644
index 00000000..4dbcc273
--- /dev/null
+++ b/formats/cbor/build.gradle
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+apply plugin: 'kotlin-multiplatform'
+apply plugin: 'kotlinx-serialization'
+apply from: rootProject.file("gradle/native-targets.gradle")
+apply from: rootProject.file("gradle/configure-source-sets.gradle")
+
+kotlin {
+
+ sourceSets {
+ commonMain {
+ dependencies {
+ api project(":kotlinx-serialization-core")
+ }
+ }
+
+ jvmTest {
+ dependencies {
+ implementation 'io.kotlintest:kotlintest:2.0.7'
+ implementation 'com.upokecenter:cbor:4.2.0'
+ implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version"
+ implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
+ implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version"
+ implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:$jackson_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
+ }
+ }
+ }
+}
+
+Java9Modularity.configureJava9ModuleInfo(project)
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/ByteString.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/ByteString.kt
new file mode 100644
index 00000000..ef57e8a8
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/ByteString.kt
@@ -0,0 +1,26 @@
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+
+/**
+ * Specifies that a [ByteArray] shall be encoded/decoded as CBOR major type 2: a byte string.
+ * For types other than [ByteArray], [ByteString] will have no effect.
+ *
+ * Example usage:
+ *
+ * ```
+ * @Serializable
+ * data class Data(
+ * @ByteString
+ * val a: ByteArray, // CBOR major type 2: a byte string.
+ *
+ * val b: ByteArray // CBOR major type 4: an array of data items.
+ * )
+ * ```
+ *
+ * See [RFC 7049 2.1. Major Types](https://tools.ietf.org/html/rfc7049#section-2.1).
+ */
+@SerialInfo
+@Target(AnnotationTarget.PROPERTY)
+@ExperimentalSerializationApi
+public annotation class ByteString
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt
new file mode 100644
index 00000000..a9bb4763
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.cbor.internal.ByteArrayInput
+import kotlinx.serialization.cbor.internal.ByteArrayOutput
+import kotlinx.serialization.cbor.internal.*
+import kotlinx.serialization.modules.*
+
+/**
+ * Implements [encoding][encodeToByteArray] and [decoding][decodeFromByteArray] classes to/from bytes
+ * using [CBOR](https://tools.ietf.org/html/rfc7049) specification.
+ * It is typically used by constructing an application-specific instance, with configured behaviour, and,
+ * if necessary, registered custom serializers (in [SerializersModule] provided by [serializersModule] constructor parameter).
+ *
+ * ### Known caveats and limitations:
+ * Supports reading collections of both definite and indefinite lengths; however,
+ * serialization always writes maps and lists as [indefinite-length](https://tools.ietf.org/html/rfc7049#section-2.2.1) ones.
+ * Does not support [optional tags](https://tools.ietf.org/html/rfc7049#section-2.4) representing datetime, bignums, etc.
+ * Fully support CBOR maps, which, unlike JSON ones, may contain keys of non-primitive types, and may produce such maps
+ * from corresponding Kotlin objects. However, other 3rd-party parsers (e.g. `jackson-dataformat-cbor`) may not accept such maps.
+ *
+ * @param encodeDefaults specifies whether default values of Kotlin properties are encoded.
+ * False by default; meaning that properties with values equal to defaults will be elided.
+ * @param ignoreUnknownKeys specifies if unknown CBOR elements should be ignored (skipped) when decoding.
+ */
+@ExperimentalSerializationApi
+public sealed class Cbor(
+ internal val encodeDefaults: Boolean,
+ internal val ignoreUnknownKeys: Boolean,
+ override val serializersModule: SerializersModule
+) : BinaryFormat {
+
+ /**
+ * The default instance of [Cbor]
+ */
+ public companion object Default : Cbor(false, false, EmptySerializersModule)
+
+ override fun <T> encodeToByteArray(serializer: SerializationStrategy<T>, value: T): ByteArray {
+ val output = ByteArrayOutput()
+ val dumper = CborWriter(this, CborEncoder(output))
+ dumper.encodeSerializableValue(serializer, value)
+ return output.toByteArray()
+ }
+
+ override fun <T> decodeFromByteArray(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T {
+ val stream = ByteArrayInput(bytes)
+ val reader = CborReader(this, CborDecoder(stream))
+ return reader.decodeSerializableValue(deserializer)
+ }
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private class CborImpl(encodeDefaults: Boolean, ignoreUnknownKeys: Boolean, serializersModule: SerializersModule) :
+ Cbor(encodeDefaults, ignoreUnknownKeys, serializersModule)
+
+/**
+ * Creates an instance of [Cbor] configured from the optionally given [Cbor instance][from]
+ * and adjusted with [builderAction].
+ */
+@ExperimentalSerializationApi
+public fun Cbor(from: Cbor = Cbor, builderAction: CborBuilder.() -> Unit): Cbor {
+ val builder = CborBuilder(from)
+ builder.builderAction()
+ return CborImpl(builder.encodeDefaults, builder.ignoreUnknownKeys, builder.serializersModule)
+}
+
+/**
+ * Builder of the [Cbor] instance provided by `Cbor` factory function.
+ */
+@ExperimentalSerializationApi
+public class CborBuilder internal constructor(cbor: Cbor) {
+
+ /**
+ * Specifies whether default values of Kotlin properties should be encoded.
+ */
+ public var encodeDefaults: Boolean = cbor.encodeDefaults
+
+ /**
+ * Specifies whether encounters of unknown properties in the input CBOR
+ * should be ignored instead of throwing [SerializationException].
+ * `false` by default.
+ */
+ public var ignoreUnknownKeys: Boolean = cbor.ignoreUnknownKeys
+
+ /**
+ * Module with contextual and polymorphic serializers to be used in the resulting [Cbor] instance.
+ */
+ public var serializersModule: SerializersModule = cbor.serializersModule
+}
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborDecodingException.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborDecodingException.kt
new file mode 100644
index 00000000..b24bd5c8
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborDecodingException.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor.internal
+
+import kotlinx.serialization.*
+
+internal class CborDecodingException(message: String) : SerializationException(message)
+
+@Suppress("FunctionName")
+internal fun CborDecodingException(expected: String, foundByte: Int) =
+ CborDecodingException("Expected $expected, but found ${printByte(foundByte)}")
+
+internal fun printByte(b: Int): String {
+ val hexCode = "0123456789ABCDEF"
+ return buildString {
+ append(hexCode[b shr 4 and 0xF])
+ append(hexCode[b and 0xF])
+ }
+}
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoding.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoding.kt
new file mode 100644
index 00000000..0b7a0e0c
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoding.kt
@@ -0,0 +1,683 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.cbor.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.cbor.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import kotlin.experimental.*
+
+private const val FALSE = 0xf4
+private const val TRUE = 0xf5
+private const val NULL = 0xf6
+
+private const val NEXT_HALF = 0xf9
+private const val NEXT_FLOAT = 0xfa
+private const val NEXT_DOUBLE = 0xfb
+
+private const val BEGIN_ARRAY = 0x9f
+private const val BEGIN_MAP = 0xbf
+private const val BREAK = 0xff
+
+private const val ADDITIONAL_INFORMATION_INDEFINITE_LENGTH = 0x1f
+
+private const val HEADER_BYTE_STRING: Byte = 0b010_00000
+private const val HEADER_STRING: Byte = 0b011_00000
+private const val HEADER_NEGATIVE: Byte = 0b001_00000
+private const val HEADER_ARRAY: Int = 0b100_00000
+private const val HEADER_MAP: Int = 0b101_00000
+private const val HEADER_TAG: Int = 0b110_00000
+
+/** Value to represent an indefinite length CBOR item within a "length stack". */
+private const val LENGTH_STACK_INDEFINITE = -1
+
+private const val HALF_PRECISION_EXPONENT_BIAS = 15
+private const val HALF_PRECISION_MAX_EXPONENT = 0x1f
+private const val HALF_PRECISION_MAX_MANTISSA = 0x3ff
+
+private const val SINGLE_PRECISION_EXPONENT_BIAS = 127
+private const val SINGLE_PRECISION_MAX_EXPONENT = 0xFF
+
+private const val SINGLE_PRECISION_NORMALIZE_BASE = 0.5f
+
+// Differs from List only in start byte
+private class CborMapWriter(cbor: Cbor, encoder: CborEncoder) : CborListWriter(cbor, encoder) {
+ override fun writeBeginToken() = encoder.startMap()
+}
+
+// Writes all elements consequently, except size - CBOR supports maps and arrays of indefinite length
+private open class CborListWriter(cbor: Cbor, encoder: CborEncoder) : CborWriter(cbor, encoder) {
+ override fun writeBeginToken() = encoder.startArray()
+
+ override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean = true
+}
+
+// Writes class as map [fieldName, fieldValue]
+internal open class CborWriter(private val cbor: Cbor, protected val encoder: CborEncoder) : AbstractEncoder() {
+ override val serializersModule: SerializersModule
+ get() = cbor.serializersModule
+
+ private var encodeByteArrayAsByteString = false
+
+ @OptIn(ExperimentalSerializationApi::class)
+ override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
+ if (encodeByteArrayAsByteString && serializer.descriptor == ByteArraySerializer().descriptor) {
+ encoder.encodeByteString(value as ByteArray)
+ } else {
+ super.encodeSerializableValue(serializer, value)
+ }
+ }
+
+ override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean = cbor.encodeDefaults
+
+ protected open fun writeBeginToken() = encoder.startMap()
+
+ //todo: Write size of map or array if known
+ @OptIn(ExperimentalSerializationApi::class)
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+ val writer = when (descriptor.kind) {
+ StructureKind.LIST, is PolymorphicKind -> CborListWriter(cbor, encoder)
+ StructureKind.MAP -> CborMapWriter(cbor, encoder)
+ else -> CborWriter(cbor, encoder)
+ }
+ writer.writeBeginToken()
+ return writer
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) = encoder.end()
+
+ override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
+ encodeByteArrayAsByteString = descriptor.isByteString(index)
+ val name = descriptor.getElementName(index)
+ encoder.encodeString(name)
+ return true
+ }
+
+ override fun encodeString(value: String) = encoder.encodeString(value)
+
+ override fun encodeFloat(value: Float) = encoder.encodeFloat(value)
+ override fun encodeDouble(value: Double) = encoder.encodeDouble(value)
+
+ override fun encodeChar(value: Char) = encoder.encodeNumber(value.code.toLong())
+ override fun encodeByte(value: Byte) = encoder.encodeNumber(value.toLong())
+ override fun encodeShort(value: Short) = encoder.encodeNumber(value.toLong())
+ override fun encodeInt(value: Int) = encoder.encodeNumber(value.toLong())
+ override fun encodeLong(value: Long) = encoder.encodeNumber(value)
+
+ override fun encodeBoolean(value: Boolean) = encoder.encodeBoolean(value)
+
+ override fun encodeNull() = encoder.encodeNull()
+
+ @OptIn(ExperimentalSerializationApi::class) // KT-46731
+ override fun encodeEnum(
+ enumDescriptor: SerialDescriptor,
+ index: Int
+ ) =
+ encoder.encodeString(enumDescriptor.getElementName(index))
+}
+
+// For details of representation, see https://tools.ietf.org/html/rfc7049#section-2.1
+internal class CborEncoder(private val output: ByteArrayOutput) {
+
+ fun startArray() = output.write(BEGIN_ARRAY)
+ fun startMap() = output.write(BEGIN_MAP)
+ fun end() = output.write(BREAK)
+
+ fun encodeNull() = output.write(NULL)
+
+ fun encodeBoolean(value: Boolean) = output.write(if (value) TRUE else FALSE)
+
+ fun encodeNumber(value: Long) = output.write(composeNumber(value))
+
+ fun encodeByteString(data: ByteArray) {
+ encodeByteArray(data, HEADER_BYTE_STRING)
+ }
+
+ fun encodeString(value: String) {
+ encodeByteArray(value.encodeToByteArray(), HEADER_STRING)
+ }
+
+ private fun encodeByteArray(data: ByteArray, type: Byte) {
+ val header = composeNumber(data.size.toLong())
+ header[0] = header[0] or type
+ output.write(header)
+ output.write(data)
+ }
+
+ fun encodeFloat(value: Float) {
+ output.write(NEXT_FLOAT)
+ val bits = value.toRawBits()
+ for (i in 0..3) {
+ output.write((bits shr (24 - 8 * i)) and 0xFF)
+ }
+ }
+
+ fun encodeDouble(value: Double) {
+ output.write(NEXT_DOUBLE)
+ val bits = value.toRawBits()
+ for (i in 0..7) {
+ output.write(((bits shr (56 - 8 * i)) and 0xFF).toInt())
+ }
+ }
+
+ private fun composeNumber(value: Long): ByteArray =
+ if (value >= 0) composePositive(value.toULong()) else composeNegative(value)
+
+ private fun composePositive(value: ULong): ByteArray = when (value) {
+ in 0u..23u -> byteArrayOf(value.toByte())
+ in 24u..UByte.MAX_VALUE.toUInt() -> byteArrayOf(24, value.toByte())
+ in (UByte.MAX_VALUE.toUInt() + 1u)..UShort.MAX_VALUE.toUInt() -> encodeToByteArray(value, 2, 25)
+ in (UShort.MAX_VALUE.toUInt() + 1u)..UInt.MAX_VALUE -> encodeToByteArray(value, 4, 26)
+ else -> encodeToByteArray(value, 8, 27)
+ }
+
+ private fun encodeToByteArray(value: ULong, bytes: Int, tag: Byte): ByteArray {
+ val result = ByteArray(bytes + 1)
+ val limit = bytes * 8 - 8
+ result[0] = tag
+ for (i in 0 until bytes) {
+ result[i + 1] = ((value shr (limit - 8 * i)) and 0xFFu).toByte()
+ }
+ return result
+ }
+
+ private fun composeNegative(value: Long): ByteArray {
+ val aVal = if (value == Long.MIN_VALUE) Long.MAX_VALUE else -1 - value
+ val data = composePositive(aVal.toULong())
+ data[0] = data[0] or HEADER_NEGATIVE
+ return data
+ }
+}
+
+private class CborMapReader(cbor: Cbor, decoder: CborDecoder) : CborListReader(cbor, decoder) {
+ override fun skipBeginToken() = setSize(decoder.startMap() * 2)
+}
+
+private open class CborListReader(cbor: Cbor, decoder: CborDecoder) : CborReader(cbor, decoder) {
+ private var ind = 0
+
+ override fun skipBeginToken() = setSize(decoder.startArray())
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor) = if (!finiteMode && decoder.isEnd() || (finiteMode && ind >= size)) CompositeDecoder.DECODE_DONE else ind++
+}
+
+internal open class CborReader(private val cbor: Cbor, protected val decoder: CborDecoder) : AbstractDecoder() {
+
+ protected var size = -1
+ private set
+ protected var finiteMode = false
+ private set
+ private var readProperties: Int = 0
+
+ private var decodeByteArrayAsByteString = false
+
+ protected fun setSize(size: Int) {
+ if (size >= 0) {
+ finiteMode = true
+ this.size = size
+ }
+ }
+
+ override val serializersModule: SerializersModule
+ get() = cbor.serializersModule
+
+ protected open fun skipBeginToken() = setSize(decoder.startMap())
+
+ @OptIn(ExperimentalSerializationApi::class)
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ val re = when (descriptor.kind) {
+ StructureKind.LIST, is PolymorphicKind -> CborListReader(cbor, decoder)
+ StructureKind.MAP -> CborMapReader(cbor, decoder)
+ else -> CborReader(cbor, decoder)
+ }
+ re.skipBeginToken()
+ return re
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ if (!finiteMode) decoder.end()
+ }
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ val index = if (cbor.ignoreUnknownKeys) {
+ val knownIndex: Int
+ while (true) {
+ if (isDone()) return CompositeDecoder.DECODE_DONE
+ val elemName = decoder.nextString()
+ readProperties++
+
+ val index = descriptor.getElementIndex(elemName)
+ if (index == CompositeDecoder.UNKNOWN_NAME) {
+ decoder.skipElement()
+ } else {
+ knownIndex = index
+ break
+ }
+ }
+ knownIndex
+ } else {
+ if (isDone()) return CompositeDecoder.DECODE_DONE
+ val elemName = decoder.nextString()
+ readProperties++
+ descriptor.getElementIndexOrThrow(elemName)
+ }
+
+ decodeByteArrayAsByteString = descriptor.isByteString(index)
+ return index
+ }
+
+ @OptIn(ExperimentalSerializationApi::class)
+ override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
+ return if (decodeByteArrayAsByteString && deserializer.descriptor == ByteArraySerializer().descriptor) {
+ @Suppress("UNCHECKED_CAST")
+ decoder.nextByteString() as T
+ } else {
+ super.decodeSerializableValue(deserializer)
+ }
+ }
+
+ override fun decodeString() = decoder.nextString()
+
+ override fun decodeNotNullMark(): Boolean = !decoder.isNull()
+
+ override fun decodeDouble() = decoder.nextDouble()
+ override fun decodeFloat() = decoder.nextFloat()
+
+ override fun decodeBoolean() = decoder.nextBoolean()
+
+ override fun decodeByte() = decoder.nextNumber().toByte()
+ override fun decodeShort() = decoder.nextNumber().toShort()
+ override fun decodeChar() = decoder.nextNumber().toInt().toChar()
+ override fun decodeInt() = decoder.nextNumber().toInt()
+ override fun decodeLong() = decoder.nextNumber()
+
+ override fun decodeNull() = decoder.nextNull()
+
+ override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
+ enumDescriptor.getElementIndexOrThrow(decoder.nextString())
+
+ private fun isDone(): Boolean = !finiteMode && decoder.isEnd() || (finiteMode && readProperties >= size)
+}
+
+internal class CborDecoder(private val input: ByteArrayInput) {
+ private var curByte: Int = -1
+
+ init {
+ readByte()
+ }
+
+ private fun readByte(): Int {
+ curByte = input.read()
+ return curByte
+ }
+
+ fun isEof() = curByte == -1
+
+ private fun skipByte(expected: Int) {
+ if (curByte != expected) throw CborDecodingException("byte ${printByte(expected)}", curByte)
+ readByte()
+ }
+
+ fun isNull() = curByte == NULL
+
+ fun nextNull(): Nothing? {
+ skipOverTags()
+ skipByte(NULL)
+ return null
+ }
+
+ fun nextBoolean(): Boolean {
+ skipOverTags()
+ val ans = when (curByte) {
+ TRUE -> true
+ FALSE -> false
+ else -> throw CborDecodingException("boolean value", curByte)
+ }
+ readByte()
+ return ans
+ }
+
+ fun startArray() = startSized(BEGIN_ARRAY, HEADER_ARRAY, "array")
+
+ fun startMap() = startSized(BEGIN_MAP, HEADER_MAP, "map")
+
+ private fun startSized(unboundedHeader: Int, boundedHeaderMask: Int, collectionType: String): Int {
+ skipOverTags()
+ if (curByte == unboundedHeader) {
+ skipByte(unboundedHeader)
+ return -1
+ }
+ if ((curByte and 0b111_00000) != boundedHeaderMask)
+ throw CborDecodingException("start of $collectionType", curByte)
+ val size = readNumber().toInt()
+ readByte()
+ return size
+ }
+
+ fun isEnd() = curByte == BREAK
+
+ fun end() = skipByte(BREAK)
+
+ fun nextByteString(): ByteArray {
+ skipOverTags()
+ if ((curByte and 0b111_00000) != HEADER_BYTE_STRING.toInt())
+ throw CborDecodingException("start of byte string", curByte)
+ val arr = readBytes()
+ readByte()
+ return arr
+ }
+
+ fun nextString(): String {
+ skipOverTags()
+ if ((curByte and 0b111_00000) != HEADER_STRING.toInt())
+ throw CborDecodingException("start of string", curByte)
+ val arr = readBytes()
+ val ans = arr.decodeToString()
+ readByte()
+ return ans
+ }
+
+ private fun readBytes(): ByteArray =
+ if (curByte and 0b000_11111 == ADDITIONAL_INFORMATION_INDEFINITE_LENGTH) {
+ readByte()
+ readIndefiniteLengthBytes()
+ } else {
+ val strLen = readNumber().toInt()
+ input.readExactNBytes(strLen)
+ }
+
+ private fun skipOverTags() {
+ while ((curByte and 0b111_00000) == HEADER_TAG) {
+ readNumber() // This is the tag number
+ readByte()
+ }
+ }
+
+ fun nextNumber(): Long {
+ skipOverTags()
+ val res = readNumber()
+ readByte()
+ return res
+ }
+
+ private fun readNumber(): Long {
+ val value = curByte and 0b000_11111
+ val negative = (curByte and 0b111_00000) == HEADER_NEGATIVE.toInt()
+ val bytesToRead = when (value) {
+ 24 -> 1
+ 25 -> 2
+ 26 -> 4
+ 27 -> 8
+ else -> 0
+ }
+ if (bytesToRead == 0) {
+ return if (negative) -(value + 1).toLong()
+ else value.toLong()
+ }
+ val res = input.readExact(bytesToRead)
+ return if (negative) -(res + 1)
+ else res
+ }
+
+ private fun ByteArrayInput.readExact(bytes: Int): Long {
+ val arr = readExactNBytes(bytes)
+ var result = 0L
+ for (i in 0 until bytes) {
+ result = (result shl 8) or (arr[i].toInt() and 0xFF).toLong()
+ }
+ return result
+ }
+
+ private fun ByteArrayInput.readExactNBytes(bytesCount: Int): ByteArray {
+ if (bytesCount > availableBytes) {
+ error("Unexpected EOF, available $availableBytes bytes, requested: $bytesCount")
+ }
+ val array = ByteArray(bytesCount)
+ read(array, 0, bytesCount)
+ return array
+ }
+
+ fun nextFloat(): Float {
+ skipOverTags()
+ val res = when (curByte) {
+ NEXT_FLOAT -> Float.fromBits(readInt())
+ NEXT_HALF -> floatFromHalfBits(readShort())
+ else -> throw CborDecodingException("float header", curByte)
+ }
+ readByte()
+ return res
+ }
+
+ fun nextDouble(): Double {
+ skipOverTags()
+ val res = when (curByte) {
+ NEXT_DOUBLE -> Double.fromBits(readLong())
+ NEXT_FLOAT -> Float.fromBits(readInt()).toDouble()
+ NEXT_HALF -> floatFromHalfBits(readShort()).toDouble()
+ else -> throw CborDecodingException("double header", curByte)
+ }
+ readByte()
+ return res
+ }
+
+ private fun readLong(): Long {
+ var result = 0L
+ for (i in 0..7) {
+ val byte = input.read()
+ result = (result shl 8) or byte.toLong()
+ }
+ return result
+ }
+
+ private fun readShort(): Short {
+ val highByte = input.read()
+ val lowByte = input.read()
+ return (highByte shl 8 or lowByte).toShort()
+ }
+
+ private fun readInt(): Int {
+ var result = 0
+ for (i in 0..3) {
+ val byte = input.read()
+ result = (result shl 8) or byte
+ }
+ return result
+ }
+
+ /**
+ * Skips the current value element. Bytes are processed to determine the element type (and corresponding length), to
+ * determine how many bytes to skip.
+ *
+ * For primitive (finite length) elements (e.g. unsigned integer, text string), their length is read and
+ * corresponding number of bytes are skipped.
+ *
+ * For elements that contain children (e.g. array, map), the child count is read and added to a "length stack"
+ * (which represents the "number of elements" at each depth of the CBOR data structure). When a child element has
+ * been skipped, the "length stack" is [pruned][prune]. For indefinite length elements, a special marker is added to
+ * the "length stack" which is only popped from the "length stack" when a CBOR [break][isEnd] is encountered.
+ */
+ fun skipElement() {
+ val lengthStack = mutableListOf<Int>()
+
+ skipOverTags()
+
+ do {
+ if (isEof()) throw CborDecodingException("Unexpected EOF while skipping element")
+
+ if (isIndefinite()) {
+ lengthStack.add(LENGTH_STACK_INDEFINITE)
+ } else if (isEnd()) {
+ if (lengthStack.removeLastOrNull() != LENGTH_STACK_INDEFINITE)
+ throw CborDecodingException("next data item", curByte)
+ prune(lengthStack)
+ } else {
+ val header = curByte and 0b111_00000
+ val length = elementLength()
+ if (header == HEADER_ARRAY || header == HEADER_MAP) {
+ if (length > 0) lengthStack.add(length)
+ skipOverTags()
+ } else {
+ input.skip(length)
+ prune(lengthStack)
+ }
+ }
+
+ readByte()
+ } while (lengthStack.isNotEmpty())
+ }
+
+ /**
+ * Removes an item from the top of the [lengthStack], cascading the removal if the item represents the last item
+ * (i.e. a length value of `1`) at its stack depth.
+ *
+ * For example, pruning a [lengthStack] of `[3, 2, 1, 1]` would result in `[3, 1]`.
+ */
+ private fun prune(lengthStack: MutableList<Int>) {
+ for (i in lengthStack.lastIndex downTo 0) {
+ when (lengthStack[i]) {
+ LENGTH_STACK_INDEFINITE -> break
+ 1 -> lengthStack.removeAt(i)
+ else -> {
+ lengthStack[i] = lengthStack[i] - 1
+ break
+ }
+ }
+ }
+ }
+
+ /**
+ * Determines if [curByte] represents an indefinite length CBOR item.
+ *
+ * Per [RFC 7049: 2.2. Indefinite Lengths for Some Major Types](https://tools.ietf.org/html/rfc7049#section-2.2):
+ * > Four CBOR items (arrays, maps, byte strings, and text strings) can be encoded with an indefinite length
+ */
+ private fun isIndefinite(): Boolean {
+ val majorType = curByte and 0b111_00000
+ val value = curByte and 0b000_11111
+
+ return value == ADDITIONAL_INFORMATION_INDEFINITE_LENGTH &&
+ (majorType == HEADER_ARRAY || majorType == HEADER_MAP ||
+ majorType == HEADER_BYTE_STRING.toInt() || majorType == HEADER_STRING.toInt())
+ }
+
+ /**
+ * Determines the length of the CBOR item represented by [curByte]; length has specific meaning based on the type:
+ *
+ * | Major type | Length represents number of... |
+ * |---------------------|--------------------------------|
+ * | 0. unsigned integer | bytes |
+ * | 1. negative integer | bytes |
+ * | 2. byte string | bytes |
+ * | 3. string | bytes |
+ * | 4. array | data items (values) |
+ * | 5. map | sub-items (keys + values) |
+ * | 6. tag | bytes |
+ */
+ private fun elementLength(): Int {
+ val majorType = curByte and 0b111_00000
+ val additionalInformation = curByte and 0b000_11111
+
+ return when (majorType) {
+ HEADER_BYTE_STRING.toInt(), HEADER_STRING.toInt(), HEADER_ARRAY -> readNumber().toInt()
+ HEADER_MAP -> readNumber().toInt() * 2
+ else -> when (additionalInformation) {
+ 24 -> 1
+ 25 -> 2
+ 26 -> 4
+ 27 -> 8
+ else -> 0
+ }
+ }
+ }
+
+ /**
+ * Indefinite-length byte sequences contain an unknown number of fixed-length byte sequences (chunks).
+ *
+ * @return [ByteArray] containing all of the concatenated bytes found in the buffer.
+ */
+ private fun readIndefiniteLengthBytes(): ByteArray {
+ val byteStrings = mutableListOf<ByteArray>()
+ do {
+ byteStrings.add(readBytes())
+ readByte()
+ } while (!isEnd())
+ return byteStrings.flatten()
+ }
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private fun SerialDescriptor.getElementIndexOrThrow(name: String): Int {
+ val index = getElementIndex(name)
+ if (index == CompositeDecoder.UNKNOWN_NAME)
+ throw SerializationException("$serialName does not contain element with name '$name." +
+ " You can enable 'CborBuilder.ignoreUnknownKeys' property to ignore unknown keys")
+ return index
+}
+
+private fun Iterable<ByteArray>.flatten(): ByteArray {
+ val output = ByteArray(sumOf { it.size })
+ var position = 0
+ for (chunk in this) {
+ chunk.copyInto(output, position)
+ position += chunk.size
+ }
+
+ return output
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private fun SerialDescriptor.isByteString(index: Int): Boolean {
+ return getElementAnnotations(index).find { it is ByteString } != null
+}
+
+
+private val normalizeBaseBits = SINGLE_PRECISION_NORMALIZE_BASE.toBits()
+
+
+/*
+ * For details about half-precision floating-point numbers see https://tools.ietf.org/html/rfc7049#appendix-D
+ */
+private fun floatFromHalfBits(bits: Short): Float {
+ val intBits = bits.toInt()
+
+ val negative = (intBits and 0x8000) != 0
+ val halfExp = intBits shr 10 and HALF_PRECISION_MAX_EXPONENT
+ val halfMant = intBits and HALF_PRECISION_MAX_MANTISSA
+
+ val exp: Int
+ val mant: Int
+
+ when (halfExp) {
+ HALF_PRECISION_MAX_EXPONENT -> {
+ // if exponent maximal - value is NaN or Infinity
+ exp = SINGLE_PRECISION_MAX_EXPONENT
+ mant = halfMant
+ }
+ 0 -> {
+ if (halfMant == 0) {
+ // if exponent and mantissa are zero - value is zero
+ mant = 0
+ exp = 0
+ } else {
+ // if exponent is zero and mantissa non-zero - value denormalized. normalize it
+ var res = Float.fromBits(normalizeBaseBits + halfMant)
+ res -= SINGLE_PRECISION_NORMALIZE_BASE
+ return if (negative) -res else res
+ }
+ }
+ else -> {
+ // normalized value
+ exp = (halfExp + (SINGLE_PRECISION_EXPONENT_BIAS - HALF_PRECISION_EXPONENT_BIAS))
+ mant = halfMant
+ }
+ }
+
+ val res = Float.fromBits((exp shl 23) or (mant shl 13))
+ return if (negative) -res else res
+}
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Streams.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Streams.kt
new file mode 100644
index 00000000..0e5b477d
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Streams.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor.internal
+
+import kotlinx.serialization.*
+
+internal class ByteArrayInput(private var array: ByteArray) {
+ private var position: Int = 0
+ public val availableBytes: Int get() = array.size - position
+
+ fun read(): Int {
+ return if (position < array.size) array[position++].toInt() and 0xFF else -1
+ }
+
+ fun read(b: ByteArray, offset: Int, length: Int): Int {
+ // avoid int overflow
+ if (offset < 0 || offset > b.size || length < 0
+ || length > b.size - offset
+ ) {
+ throw IndexOutOfBoundsException()
+ }
+ // Are there any bytes available?
+ if (this.position >= array.size) {
+ return -1
+ }
+ if (length == 0) {
+ return 0
+ }
+
+ val copied = if (this.array.size - position < length) this.array.size - position else length
+ array.copyInto(destination = b, destinationOffset = offset, startIndex = position, endIndex = position + copied)
+ position += copied
+ return copied
+ }
+
+ fun skip(length: Int) {
+ position += length
+ }
+}
+
+internal class ByteArrayOutput {
+ private var array: ByteArray = ByteArray(32)
+ private var position: Int = 0
+
+ private fun ensureCapacity(elementsToAppend: Int) {
+ if (position + elementsToAppend <= array.size) {
+ return
+ }
+ val newArray = ByteArray((position + elementsToAppend).takeHighestOneBit() shl 1)
+ array.copyInto(newArray)
+ array = newArray
+ }
+
+ public fun toByteArray(): ByteArray {
+ val newArray = ByteArray(position)
+ array.copyInto(newArray, startIndex = 0, endIndex = this.position)
+ return newArray
+ }
+
+ fun write(buffer: ByteArray, offset: Int = 0, count: Int = buffer.size) {
+ // avoid int overflow
+ if (offset < 0 || offset > buffer.size || count < 0
+ || count > buffer.size - offset
+ ) {
+ throw IndexOutOfBoundsException()
+ }
+ if (count == 0) {
+ return
+ }
+
+ ensureCapacity(count)
+ buffer.copyInto(
+ destination = array,
+ destinationOffset = this.position,
+ startIndex = offset,
+ endIndex = offset + count
+ )
+ this.position += count
+ }
+
+ fun write(byteValue: Int) {
+ ensureCapacity(1)
+ array[position++] = byteValue.toByte()
+ }
+}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/HexConverter.kt b/formats/cbor/commonTest/src/kotlinx/serialization/HexConverter.kt
new file mode 100644
index 00000000..9ee97f32
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/HexConverter.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+internal object HexConverter {
+ private const val hexCode = "0123456789ABCDEF"
+
+ fun parseHexBinary(s: String): ByteArray {
+ val len = s.length
+ require(len % 2 == 0) { "HexBinary string must be even length" }
+ val bytes = ByteArray(len / 2)
+ var i = 0
+
+ while (i < len) {
+ val h = hexToInt(s[i])
+ val l = hexToInt(s[i + 1])
+ require(!(h == -1 || l == -1)) { "Invalid hex chars: ${s[i]}${s[i + 1]}" }
+
+ bytes[i / 2] = ((h shl 4) + l).toByte()
+ i += 2
+ }
+
+ return bytes
+ }
+
+ private fun hexToInt(ch: Char): Int = when (ch) {
+ in '0'..'9' -> ch - '0'
+ in 'A'..'F' -> ch - 'A' + 10
+ in 'a'..'f' -> ch - 'a' + 10
+ else -> -1
+ }
+
+ fun printHexBinary(data: ByteArray, lowerCase: Boolean = false): String {
+ val r = StringBuilder(data.size * 2)
+ for (b in data) {
+ r.append(hexCode[b.toInt() shr 4 and 0xF])
+ r.append(hexCode[b.toInt() and 0xF])
+ }
+ return if (lowerCase) r.toString().lowercase() else r.toString()
+ }
+
+ fun toHexString(n: Int): String {
+ val arr = ByteArray(4)
+ for (i in 0 until 4) {
+ arr[i] = (n shr (24 - i * 8)).toByte()
+ }
+ return printHexBinary(arr, true).trimStart('0').takeIf { it.isNotEmpty() } ?: "0"
+ }
+}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt b/formats/cbor/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
new file mode 100644
index 00000000..86aafe42
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.modules.*
+import kotlin.native.concurrent.*
+
+@Serializable
+abstract class SimpleAbstract
+
+@Serializable
+data class SimpleIntInheritor(val i: Int, val s: String) : SimpleAbstract()
+
+@Serializable
+data class SimpleStringInheritor(val s: String, val i: Int) : SimpleAbstract()
+
+@Serializable
+data class PolyBox(@Polymorphic val boxed: SimpleAbstract)
+
+@SharedImmutable
+val SimplePolymorphicModule = SerializersModule {
+ polymorphic(SimpleAbstract::class) {
+ subclass(SimpleIntInheritor.serializer())
+ subclass(SimpleStringInheritor.serializer())
+ }
+}
+
+@Serializable
+sealed class SimpleSealed {
+ @Serializable
+ public data class SubSealedA(val s: String) : SimpleSealed()
+
+ @Serializable
+ public data class SubSealedB(val i: Int) : SimpleSealed()
+}
+
+@Serializable
+data class SealedBox(val boxed: List<SimpleSealed>)
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/TestUtilities.kt b/formats/cbor/commonTest/src/kotlinx/serialization/TestUtilities.kt
new file mode 100644
index 00000000..c576d1a5
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/TestUtilities.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlin.test.*
+
+internal inline fun <reified T : Any> assertSerializedToBinaryAndRestored(
+ original: T,
+ serializer: KSerializer<T>,
+ format: BinaryFormat,
+ printResult: Boolean = false,
+ hexResultToCheck: String? = null
+) {
+ val bytes = format.encodeToByteArray(serializer, original)
+ val hexString = HexConverter.printHexBinary(bytes, lowerCase = true)
+ if (printResult) {
+ println("[Serialized form] $hexString")
+ }
+ if (hexResultToCheck != null) {
+ assertEquals(
+ hexResultToCheck.lowercase(),
+ hexString,
+ "Expected serialized binary to be equal in hex representation"
+ )
+ }
+ val restored = format.decodeFromByteArray(serializer, bytes)
+ if (printResult) println("[Restored form] $restored")
+ assertEquals(original, restored)
+}
+
+inline fun <reified T : Throwable> assertFailsWithMessage(message: String, block: () -> Unit) {
+ val exception = assertFailsWith(T::class, null, block)
+ assertTrue(exception.message!!.contains(message), "Expected message '${exception.message}' to contain substring '$message'")
+}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborNumberEncodingTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborNumberEncodingTest.kt
new file mode 100644
index 00000000..c8073196
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborNumberEncodingTest.kt
@@ -0,0 +1,215 @@
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.decodeFromByteArray
+import kotlinx.serialization.encodeToByteArray
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class CborNumberEncodingTest {
+
+ // 0-23 packs into a single byte
+ @Test
+ fun testEncodingLengthOfTinyNumbers() {
+ val tinyNumbers = listOf(0, 1, 23)
+ for (number in tinyNumbers) {
+ assertEquals(
+ expected = 1,
+ actual = Cbor.encodeToByteArray(number).size,
+ "when encoding value '$number'"
+ )
+ }
+ }
+
+ // 24..(2^8-1) packs into 2 bytes
+ @Test
+ fun testEncodingLengthOf8BitNumbers() {
+ val tinyNumbers = listOf(24, 127, 128, 255)
+ for (number in tinyNumbers) {
+ assertEquals(
+ expected = 2,
+ actual = Cbor.encodeToByteArray(number).size,
+ "when encoding value '$number'"
+ )
+ }
+ }
+
+ // 2^8..(2^16-1) packs into 3 bytes
+ @Test
+ fun testEncodingLengthOf16BitNumbers() {
+ val tinyNumbers = listOf(256, 32767, 32768, 65535)
+ for (number in tinyNumbers) {
+ assertEquals(
+ expected = 3,
+ actual = Cbor.encodeToByteArray(number).size,
+ "when encoding value '$number'"
+ )
+ }
+ }
+
+ // 2^16..(2^32-1) packs into 5 bytes
+ @Test
+ fun testEncodingLengthOf32BitNumbers() {
+ val tinyNumbers = listOf(65536, 2147483647, 2147483648, 4294967295)
+ for (number in tinyNumbers) {
+ assertEquals(
+ expected = 5,
+ actual = Cbor.encodeToByteArray(number).size,
+ "when encoding value '$number'"
+ )
+ }
+ }
+
+ // 2^32+ packs into 9 bytes
+ @Test
+ fun testEncodingLengthOfLargeNumbers() {
+ val tinyNumbers = listOf(4294967296, 8589934592)
+ for (number in tinyNumbers) {
+ assertEquals(
+ expected = 9,
+ actual = Cbor.encodeToByteArray(number).size,
+ "when encoding value '$number'"
+ )
+ }
+ }
+
+ @Test
+ fun testEncodingLargestPositiveTinyNumber() {
+ assertEquals(
+ expected = byteArrayOf(23).toList(),
+ actual = Cbor.encodeToByteArray(23).toList(),
+ )
+ }
+
+ @Test
+ fun testDecodingLargestPositiveTinyNumber() {
+ assertEquals(
+ expected = 23,
+ actual = Cbor.decodeFromByteArray(byteArrayOf(23)),
+ )
+ }
+
+
+ @Test
+ fun testEncodingLargestNegativeTinyNumber() {
+ assertEquals(
+ expected = byteArrayOf(55).toList(),
+ actual = Cbor.encodeToByteArray(-24).toList(),
+ )
+ }
+
+ @Test
+ fun testDecodingLargestNegativeTinyNumber() {
+ assertEquals(
+ expected = -24,
+ actual = Cbor.decodeFromByteArray(byteArrayOf(55)),
+ )
+ }
+
+ @Test
+ fun testEncodingLargestPositive8BitNumber() {
+ val bytes = listOf(24, 255).map { it.toByte() }
+ assertEquals(
+ expected = bytes,
+ actual = Cbor.encodeToByteArray(255).toList(),
+ )
+ }
+
+ @Test
+ fun testDecodingLargestPositive8BitNumber() {
+ val bytes = listOf(24, 255).map { it.toByte() }.toByteArray()
+ assertEquals(
+ expected = 255,
+ actual = Cbor.decodeFromByteArray(bytes),
+ )
+ }
+
+ @Test
+ fun testEncodingLargestNegative8BitNumber() {
+ val bytes = listOf(56, 255).map { it.toByte() }
+ assertEquals(
+ expected = bytes,
+ actual = Cbor.encodeToByteArray(-256).toList(),
+ )
+ }
+
+ @Test
+ fun testDecodingLargestNegative8BitNumber() {
+ val bytes = listOf(56, 255).map { it.toByte() }.toByteArray()
+ assertEquals(
+ expected = -256,
+ actual = Cbor.decodeFromByteArray(bytes),
+ )
+ }
+
+ @Test
+ fun testEncodingLargestPositive16BitNumber() {
+ val bytes = listOf(25, 255, 255).map { it.toByte() }
+ assertEquals(
+ expected = bytes,
+ actual = Cbor.encodeToByteArray(65535).toList(),
+ )
+ }
+
+ @Test
+ fun testDecodingLargestPositive16BitNumber() {
+ val bytes = listOf(25, 255, 255).map { it.toByte() }.toByteArray()
+ assertEquals(
+ expected = 65535,
+ actual = Cbor.decodeFromByteArray(bytes),
+ )
+ }
+
+ @Test
+ fun testEncodingLargestNegative16BitNumber() {
+ val bytes = listOf(57, 255, 255).map { it.toByte() }
+ assertEquals(
+ expected = bytes,
+ actual = Cbor.encodeToByteArray(-65536).toList(),
+ )
+ }
+
+ @Test
+ fun testDecodingLargestNegative16BitNumber() {
+ val bytes = listOf(57, 255, 255).map { it.toByte() }.toByteArray()
+ assertEquals(
+ expected = -65536,
+ actual = Cbor.decodeFromByteArray(bytes),
+ )
+ }
+
+ @Test
+ fun testEncodingLargestPositive32BitNumber() {
+ val bytes = listOf(26, 255, 255, 255, 255).map { it.toByte() }
+ assertEquals(
+ expected = bytes,
+ actual = Cbor.encodeToByteArray(4294967295).toList(),
+ )
+ }
+
+ @Test
+ fun testDecodingLargestPositive32BitNumber() {
+ val bytes = listOf(26, 255, 255, 255, 255).map { it.toByte() }.toByteArray()
+ assertEquals(
+ expected = 4294967295,
+ actual = Cbor.decodeFromByteArray(bytes),
+ )
+ }
+
+ @Test
+ fun testEncodingLargestNegative32BitNumber() {
+ val bytes = listOf(58, 255, 255, 255, 255).map { it.toByte() }
+ assertEquals(
+ expected = bytes,
+ actual = Cbor.encodeToByteArray(-4294967296).toList(),
+ )
+ }
+
+ @Test
+ fun testDecodingLargestNegative32BitNumber() {
+ val bytes = listOf(58, 255, 255, 255, 255).map { it.toByte() }.toByteArray()
+ assertEquals(
+ expected = -4294967296,
+ actual = Cbor.decodeFromByteArray(bytes),
+ )
+ }
+}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborPolymorphismTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborPolymorphismTest.kt
new file mode 100644
index 00000000..156f4714
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborPolymorphismTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class CborPolymorphismTest {
+ @Serializable
+ sealed class A {
+ @Serializable
+ data class B(val b: String) : A()
+ }
+
+ val cbor = Cbor { serializersModule = SimplePolymorphicModule }
+
+ @Test
+ fun testSealedWithOneSubclass() {
+ assertSerializedToBinaryAndRestored(
+ A.B("bbb"),
+ A.serializer(),
+ cbor,
+ hexResultToCheck = "9f78336b6f746c696e782e73657269616c697a6174696f6e2e63626f722e43626f72506f6c796d6f72706869736d546573742e412e42bf616263626262ffff"
+ )
+ }
+
+ @Test
+ fun testSealedWithMultipleSubclasses() {
+ val obj = SealedBox(
+ listOf(
+ SimpleSealed.SubSealedB(33),
+ SimpleSealed.SubSealedA("str")
+ )
+ )
+ assertSerializedToBinaryAndRestored(obj, SealedBox.serializer(), cbor)
+ }
+
+ @Test
+ fun testOpenPolymorphism() {
+ val obj = PolyBox(
+ SimpleStringInheritor(
+ "str",
+ 133
+ )
+ )
+ assertSerializedToBinaryAndRestored(obj, PolyBox.serializer(), cbor)
+ }
+}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborReaderTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborReaderTest.kt
new file mode 100644
index 00000000..edbe5e62
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborReaderTest.kt
@@ -0,0 +1,737 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlinx.serialization.SimpleSealed.*
+import kotlinx.serialization.cbor.internal.*
+import kotlin.test.*
+
+class CborReaderTest {
+
+ private val ignoreUnknownKeys = Cbor { ignoreUnknownKeys = true }
+
+ private fun withDecoder(input: String, block: CborDecoder.() -> Unit) {
+ val bytes = HexConverter.parseHexBinary(input.uppercase())
+ CborDecoder(ByteArrayInput(bytes)).block()
+ }
+
+ @Test
+ fun testDecodeIntegers() {
+ withDecoder("0C1903E8") {
+ assertEquals(12L, nextNumber())
+ assertEquals(1000L, nextNumber())
+ }
+ withDecoder("203903e7") {
+ assertEquals(-1L, nextNumber())
+ assertEquals(-1000L, nextNumber())
+ }
+ }
+
+ @Test
+ fun testDecodeStrings() {
+ withDecoder("6568656C6C6F") {
+ assertEquals("hello", nextString())
+ }
+ withDecoder("7828737472696E672074686174206973206C6F6E676572207468616E2032332063686172616374657273") {
+ assertEquals("string that is longer than 23 characters", nextString())
+ }
+ }
+
+ @Test
+ fun testDecodeDoubles() {
+ withDecoder("fb7e37e43c8800759c") {
+ assertEquals(1e+300, nextDouble())
+ }
+ withDecoder("fa47c35000") {
+ assertEquals(100000.0f, nextFloat())
+ }
+ }
+
+ @Test
+ fun testDecodeSimpleObject() {
+ assertEquals(Simple("str"), Cbor.decodeFromHexString(Simple.serializer(), "bf616163737472ff"))
+ }
+
+ @Test
+ fun testDecodeComplicatedObject() {
+ val test = TypesUmbrella(
+ "Hello, world!",
+ 42,
+ null,
+ listOf("a", "b"),
+ mapOf(1 to true, 2 to false),
+ Simple("lol"),
+ listOf(Simple("kek")),
+ HexConverter.parseHexBinary("cafe"),
+ HexConverter.parseHexBinary("cafe")
+ )
+ // with maps, lists & strings of indefinite length
+ assertEquals(test, Cbor.decodeFromHexString(
+ TypesUmbrella.serializer(),
+ "bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffff6a62797465537472696e675f42cafeff696279746541727261799f383521ffff"
+ )
+ )
+ // with maps, lists & strings of definite length
+ assertEquals(test, Cbor.decodeFromHexString(
+ TypesUmbrella.serializer(),
+ "a9646c6973748261616162686e756c6c61626c65f6636d6170a202f401f56169182a6a696e6e6572734c69737481a16161636b656b637374726d48656c6c6f2c20776f726c642165696e6e6572a16161636c6f6c6a62797465537472696e6742cafe6962797465417272617982383521"
+ )
+ )
+ }
+
+ /**
+ * Test using example shown on page 11 of [RFC 7049 2.2.2](https://tools.ietf.org/html/rfc7049#section-2.2.2):
+ *
+ * ```
+ * 0b010_11111 0b010_00100 0xaabbccdd 0b010_00011 0xeeff99 0b111_11111
+ *
+ * 5F -- Start indefinite-length byte string
+ * 44 -- Byte string of length 4
+ * aabbccdd -- Bytes content
+ * 43 -- Byte string of length 3
+ * eeff99 -- Bytes content
+ * FF -- "break"
+ *
+ * After decoding, this results in a single byte string with seven
+ * bytes: 0xaabbccddeeff99.
+ * ```
+ */
+ @Test
+ fun testRfc7049IndefiniteByteStringExample() {
+ withDecoder(input = "5F44aabbccdd43eeff99FF") {
+ assertEquals(
+ expected = "aabbccddeeff99",
+ actual = HexConverter.printHexBinary(nextByteString(), lowerCase = true)
+ )
+ }
+ }
+
+ @Test
+ fun testReadByteStringWhenNullable() {
+ /* A1 # map(1)
+ * 6A # text(10)
+ * 62797465537472696E67 # "byteString"
+ * 44 # bytes(4)
+ * 01020304 # "\x01\x02\x03\x04"
+ */
+ assertEquals(
+ expected = NullableByteString(byteArrayOf(1, 2, 3, 4)),
+ actual = Cbor.decodeFromHexString(
+ deserializer = NullableByteString.serializer(),
+ hex = "a16a62797465537472696e674401020304"
+ )
+ )
+
+ /* A1 # map(1)
+ * 6A # text(10)
+ * 62797465537472696E67 # "byteString"
+ * F6 # primitive(22)
+ */
+ assertEquals(
+ expected = NullableByteString(byteString = null),
+ actual = Cbor.decodeFromHexString(
+ deserializer = NullableByteString.serializer(),
+ hex = "a16a62797465537472696e67f6"
+ )
+ )
+ }
+
+ /**
+ * CBOR hex data represents serialized versions of [TypesUmbrella] (which does **not** have a root property 'a') so
+ * decoding to [Simple] (which has the field 'a') is expected to fail.
+ */
+ @Test
+ fun testIgnoreUnknownKeysFailsWhenCborDataIsMissingKeysThatArePresentInKotlinClass() {
+ // with maps & lists of indefinite length
+ assertFailsWithMessage<SerializationException>("Field 'a' is required") {
+ ignoreUnknownKeys.decodeFromHexString(
+ Simple.serializer(),
+ "bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffffff"
+ )
+ }
+
+ // with maps & lists of definite length
+ assertFailsWithMessage<SerializationException>("Field 'a' is required") {
+ ignoreUnknownKeys.decodeFromHexString(
+ Simple.serializer(),
+ "a7646c6973748261616162686e756c6c61626c65f6636d6170a202f401f56169182a6a696e6e6572734c69737481a16161636b656b637374726d48656c6c6f2c20776f726c642165696e6e6572a16161636c6f6c"
+ )
+ }
+ }
+
+ @Test
+ fun testIgnoreUnknownKeysFailsWhenDecodingIncompleteCbor() {
+ /* A3 # map(3)
+ * 63 # text(3)
+ * 737472 # "str"
+ * 66 # text(6)
+ * 737472696E67 # "string"
+ * 61 # text(1)
+ * 69 # "i"
+ * 00 # unsigned(0)
+ * 66 # text(6)
+ * 69676E6F7265 # "ignore"
+ * (missing value associated with "ignore" key)
+ */
+ assertFailsWithMessage<CborDecodingException>("Unexpected EOF while skipping element") {
+ ignoreUnknownKeys.decodeFromHexString(
+ TypesUmbrella.serializer(),
+ "a36373747266737472696e676169006669676e6f7265"
+ )
+ }
+
+ /* A3 # map(3)
+ * 63 # text(3)
+ * 737472 # "str"
+ * 66 # text(6)
+ * 737472696E67 # "string"
+ * 61 # text(1)
+ * 69 # "i"
+ * 00 # unsigned(0)
+ * 66 # text(6)
+ * 69676E6F7265 # "ignore"
+ * A2 # map(2)
+ * (missing map contents associated with "ignore" key)
+ */
+ assertFailsWithMessage<CborDecodingException>("Unexpected EOF while skipping element") {
+ ignoreUnknownKeys.decodeFromHexString(
+ TypesUmbrella.serializer(),
+ "a36373747266737472696e676169006669676e6f7265a2"
+ )
+ }
+ }
+
+ @Test
+ fun testIgnoreUnknownKeysFailsWhenEncounteringPreemptiveBreak() {
+ /* A3 # map(3)
+ * 63 # text(3)
+ * 737472 # "str"
+ * 66 # text(6)
+ * 737472696E67 # "string"
+ * 66 # text(6)
+ * 69676E6F7265 # "ignore"
+ * FF # primitive(*)
+ */
+ assertFailsWithMessage<CborDecodingException>("Expected next data item, but found FF") {
+ ignoreUnknownKeys.decodeFromHexString(
+ TypesUmbrella.serializer(),
+ "a36373747266737472696e676669676e6f7265ff"
+ )
+ }
+ }
+
+ /**
+ * Tests skipping unknown keys associated with values of the following CBOR types:
+ * - Major type 0: an unsigned integer
+ * - Major type 1: a negative integer
+ * - Major type 2: a byte string
+ * - Major type 3: a text string
+ */
+ @Test
+ fun testSkipPrimitives() {
+ /* A4 # map(4)
+ * 61 # text(1)
+ * 61 # "a"
+ * 1B FFFFFFFFFFFFFFFF # unsigned(18446744073709551615)
+ * 61 # text(1)
+ * 62 # "b"
+ * 20 # negative(0)
+ * 61 # text(1)
+ * 63 # "c"
+ * 42 # bytes(2)
+ * CAFE # "\xCA\xFE"
+ * 61 # text(1)
+ * 64 # "d"
+ * 6B # text(11)
+ * 48656C6C6F20776F726C64 # "Hello world"
+ */
+ withDecoder("a461611bffffffffffffffff616220616342cafe61646b48656c6c6f20776f726c64") {
+ expectMap(size = 4)
+ expect("a")
+ skipElement() // unsigned(18446744073709551615)
+ expect("b")
+ skipElement() // negative(0)
+ expect("c")
+ skipElement() // "\xCA\xFE"
+ expect("d")
+ skipElement() // "Hello world"
+ expectEof()
+ }
+ }
+
+ /**
+ * Tests skipping unknown keys associated with values (that are empty) of the following CBOR types:
+ * - Major type 2: a byte string
+ * - Major type 3: a text string
+ */
+ @Test
+ fun testSkipEmptyPrimitives() {
+ /* A2 # map(2)
+ * 61 # text(1)
+ * 61 # "a"
+ * 40 # bytes(0)
+ * # ""
+ * 61 # text(1)
+ * 62 # "b"
+ * 60 # text(0)
+ * # ""
+ */
+ withDecoder("a2616140616260") {
+ expectMap(size = 2)
+ expect("a")
+ skipElement() // bytes(0)
+ expect("b")
+ skipElement() // text(0)
+ expectEof()
+ }
+ }
+
+ /**
+ * Tests skipping unknown keys associated with values of the following CBOR types:
+ * - Major type 4: an array of data items
+ * - Major type 5: a map of pairs of data items
+ */
+ @Test
+ fun testSkipCollections() {
+ /* A2 # map(2)
+ * 61 # text(1)
+ * 61 # "a"
+ * 83 # array(3)
+ * 01 # unsigned(1)
+ * 18 FF # unsigned(255)
+ * 1A 00010000 # unsigned(65536)
+ * 61 # text(1)
+ * 62 # "b"
+ * A2 # map(2)
+ * 61 # text(1)
+ * 78 # "x"
+ * 67 # text(7)
+ * 6B6F746C696E78 # "kotlinx"
+ * 61 # text(1)
+ * 79 # "y"
+ * 6D # text(13)
+ * 73657269616C697A6174696F6E # "serialization"
+ */
+ withDecoder("a26161830118ff1a000100006162a26178676b6f746c696e7861796d73657269616c697a6174696f6e") {
+ expectMap(size = 2)
+ expect("a")
+ skipElement() // [1, 255, 65536]
+ expect("b")
+ skipElement() // {"x": "kotlinx", "y": "serialization"}
+ expectEof()
+ }
+ }
+
+ /**
+ * Tests skipping unknown keys associated with values (empty collections) of the following CBOR types:
+ * - Major type 4: an array of data items
+ * - Major type 5: a map of pairs of data items
+ */
+ @Test
+ fun testSkipEmptyCollections() {
+ /* A2 # map(2)
+ * 61 # text(1)
+ * 61 # "a"
+ * 80 # array(0)
+ * 61 # text(1)
+ * 62 # "b"
+ * A0 # map(0)
+ */
+ withDecoder("a26161806162a0") {
+ expectMap(size = 2)
+ expect("a")
+ skipElement() // [1, 255, 65536]
+ expect("b")
+ skipElement() // {"x": "kotlinx", "y": "serialization"}
+ expectEof()
+ }
+ }
+
+ /**
+ * Tests skipping unknown keys associated with **indefinite length** values of the following CBOR types:
+ * - Major type 2: a byte string
+ * - Major type 3: a text string
+ * - Major type 4: an array of data items
+ * - Major type 5: a map of pairs of data items
+ */
+ @Test
+ fun testSkipIndefiniteLength() {
+ /* A4 # map(4)
+ * 61 # text(1)
+ * 61 # "a"
+ * 5F # bytes(*)
+ * 42 # bytes(2)
+ * CAFE # "\xCA\xFE"
+ * 43 # bytes(3)
+ * 010203 # "\x01\x02\x03"
+ * FF # primitive(*)
+ * 61 # text(1)
+ * 62 # "b"
+ * 7F # text(*)
+ * 66 # text(6)
+ * 48656C6C6F20 # "Hello "
+ * 65 # text(5)
+ * 776F726C64 # "world"
+ * FF # primitive(*)
+ * 61 # text(1)
+ * 63 # "c"
+ * 9F # array(*)
+ * 67 # text(7)
+ * 6B6F746C696E78 # "kotlinx"
+ * 6D # text(13)
+ * 73657269616C697A6174696F6E # "serialization"
+ * FF # primitive(*)
+ * 61 # text(1)
+ * 64 # "d"
+ * BF # map(*)
+ * 61 # text(1)
+ * 31 # "1"
+ * 01 # unsigned(1)
+ * 61 # text(1)
+ * 32 # "2"
+ * 02 # unsigned(2)
+ * 61 # text(1)
+ * 33 # "3"
+ * 03 # unsigned(3)
+ * FF # primitive(*)
+ */
+ withDecoder("a461615f42cafe43010203ff61627f6648656c6c6f2065776f726c64ff61639f676b6f746c696e786d73657269616c697a6174696f6eff6164bf613101613202613303ff") {
+ expectMap(size = 4)
+ expect("a")
+ skipElement() // "\xCA\xFE\x01\x02\x03"
+ expect("b")
+ skipElement() // "Hello world"
+ expect("c")
+ skipElement() // ["kotlinx", "serialization"]
+ expect("d")
+ skipElement() // {"1": 1, "2": 2, "3": 3}
+ expectEof()
+ }
+ }
+
+ /**
+ * Tests that skipping unknown keys also skips over associated tags.
+ *
+ * Includes tags on the key, tags on the value, and tags on both key and value.
+ */
+ @Test
+ fun testSkipTags() {
+ /*
+ * A4 # map(4)
+ * 61 # text(1)
+ * 61 # "a"
+ * CC # tag(12)
+ * 1B FFFFFFFFFFFFFFFF # unsigned(18446744073709551615)
+ * D8 22 # tag(34)
+ * 61 # text(1)
+ * 62 # "b"
+ * 20 # negative(0)
+ * D8 38 # tag(56)
+ * 61 # text(1)
+ * 63 # "c"
+ * D8 4E # tag(78)
+ * 42 # bytes(2)
+ * CAFE # "\xCA\xFE"
+ * 61 # text(1)
+ * 64 # "d"
+ * D8 5A # tag(90)
+ * CC # tag(12)
+ * 6B # text(11)
+ * 48656C6C6F20776F726C64 # "Hello world"
+ */
+ withDecoder("A46161CC1BFFFFFFFFFFFFFFFFD822616220D8386163D84E42CAFE6164D85ACC6B48656C6C6F20776F726C64") {
+ expectMap(size = 4)
+ expect("a")
+ skipElement() // unsigned(18446744073709551615)
+ expect("b")
+ skipElement() // negative(0)
+ expect("c")
+ skipElement() // "\xCA\xFE"
+ expect("d")
+ skipElement() // "Hello world"
+ expectEof()
+ }
+ }
+
+ @Test
+ fun testDecodeCborWithUnknownField() {
+ assertEquals(
+ expected = Simple("123"),
+ actual = ignoreUnknownKeys.decodeFromHexString(
+ deserializer = Simple.serializer(),
+
+ /* BF # map(*)
+ * 61 # text(1)
+ * 61 # "a"
+ * 63 # text(3)
+ * 313233 # "123"
+ * 61 # text(1)
+ * 62 # "b"
+ * 63 # text(3)
+ * 393837 # "987"
+ * FF # primitive(*)
+ */
+ hex = "bf616163313233616263393837ff"
+ )
+ )
+ }
+
+ @Test
+ fun testDecodeCborWithUnknownNestedIndefiniteFields() {
+ assertEquals(
+ expected = Simple("123"),
+ actual = ignoreUnknownKeys.decodeFromHexString(
+ deserializer = Simple.serializer(),
+
+ /* BF # map(*)
+ * 61 # text(1)
+ * 61 # "a"
+ * 63 # text(3)
+ * 313233 # "123"
+ * 61 # text(1)
+ * 62 # "b"
+ * BF # map(*)
+ * 7F # text(*)
+ * 61 # text(1)
+ * 78 # "x"
+ * FF # primitive(*)
+ * A1 # map(1)
+ * 61 # text(1)
+ * 79 # "y"
+ * 0A # unsigned(10)
+ * FF # primitive(*)
+ * 61 # text(1)
+ * 63 # "c"
+ * 9F # array(*)
+ * 01 # unsigned(1)
+ * 02 # unsigned(2)
+ * 03 # unsigned(3)
+ * FF # primitive(*)
+ * FF # primitive(*)
+ */
+ hex = "bf6161633132336162bf7f6178ffa161790aff61639f010203ffff"
+ )
+ )
+ }
+
+ /**
+ * The following CBOR diagnostic output demonstrates the additional fields (prefixed with `+` in front of each line)
+ * present in the encoded CBOR data that does not have associated fields in the Kotlin classes (they will be skipped
+ * over with `ignoreUnknownKeys` is enabled).
+ *
+ * ```diff
+ * {
+ * + "extra": [
+ * + 9,
+ * + 8,
+ * + 7
+ * + ],
+ * "boxed": [
+ * [
+ * "kotlinx.serialization.SimpleSealed.SubSealedA",
+ * {
+ * "s": "a",
+ * + "newA": {
+ * + "x": 1,
+ * + "y": 2
+ * + }
+ * }
+ * ],
+ * [
+ * "kotlinx.serialization.SimpleSealed.SubSealedB",
+ * {
+ * "i": 1
+ * }
+ * ]
+ * ]
+ * }
+ * ```
+ */
+ @Test
+ fun testDecodeCborWithUnknownKeysInSealedClasses() {
+ /* BF # map(*)
+ * 65 # text(5)
+ * 6578747261 # "extra"
+ * 83 # array(3)
+ * 09 # unsigned(9)
+ * 08 # unsigned(8)
+ * 07 # unsigned(7)
+ * 65 # text(5)
+ * 626F786564 # "boxed"
+ * 9F # array(*)
+ * 9F # array(*)
+ * 78 2D # text(45)
+ * 6B6F746C696E782E73657269616C697A6174696F6E2E53696D706C655365616C65642E5375625365616C656441 # "kotlinx.serialization.SimpleSealed.SubSealedA"
+ * BF # map(*)
+ * 61 # text(1)
+ * 73 # "s"
+ * 61 # text(1)
+ * 61 # "a"
+ * 64 # text(4)
+ * 6E657741 # "newA"
+ * BF # map(*)
+ * 61 # text(1)
+ * 78 # "x"
+ * 01 # unsigned(1)
+ * 61 # text(1)
+ * 79 # "y"
+ * 02 # unsigned(2)
+ * FF # primitive(*)
+ * FF # primitive(*)
+ * FF # primitive(*)
+ * 9F # array(*)
+ * 78 2D # text(45)
+ * 6B6F746C696E782E73657269616C697A6174696F6E2E53696D706C655365616C65642E5375625365616C656442 # "kotlinx.serialization.SimpleSealed.SubSealedB"
+ * BF # map(*)
+ * 61 # text(1)
+ * 69 # "i"
+ * 01 # unsigned(1)
+ * FF # primitive(*)
+ * FF # primitive(*)
+ * FF # primitive(*)
+ * FF # primitive(*)
+ */
+
+ assertEquals(
+ expected = SealedBox(
+ listOf(
+ SubSealedA("a"),
+ SubSealedB(1)
+ )
+ ),
+ actual = ignoreUnknownKeys.decodeFromHexString(
+ SealedBox.serializer(),
+ "bf6565787472618309080765626f7865649f9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656441bf61736161646e657741bf617801617902ffffff9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656442bf616901ffffffff"
+ )
+ )
+ }
+
+ @Test
+ fun testReadCustomByteString() {
+ assertEquals(
+ expected = TypeWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
+ actual = Cbor.decodeFromHexString("bf617843112233ff")
+ )
+ }
+
+ @Test
+ fun testReadNullableCustomByteString() {
+ assertEquals(
+ expected = TypeWithNullableCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
+ actual = Cbor.decodeFromHexString("bf617843112233ff")
+ )
+ }
+
+ @Test
+ fun testReadNullCustomByteString() {
+ assertEquals(
+ expected = TypeWithNullableCustomByteString(null),
+ actual = Cbor.decodeFromHexString("bf6178f6ff")
+ )
+ }
+
+ @Test
+ fun testIgnoresTagsOnStrings() {
+ /*
+ * 84 # array(4)
+ * 68 # text(8)
+ * 756E746167676564 # "untagged"
+ * C0 # tag(0)
+ * 68 # text(8)
+ * 7461676765642D30 # "tagged-0"
+ * D8 F5 # tag(245)
+ * 6A # text(10)
+ * 7461676765642D323435 # "tagged-244"
+ * D9 3039 # tag(12345)
+ * 6C # text(12)
+ * 7461676765642D3132333435 # "tagged-12345"
+ *
+ */
+ withDecoder("8468756E746167676564C0687461676765642D30D8F56A7461676765642D323435D930396C7461676765642D3132333435") {
+ assertEquals(4, startArray())
+ assertEquals("untagged", nextString())
+ assertEquals("tagged-0", nextString())
+ assertEquals("tagged-245", nextString())
+ assertEquals("tagged-12345", nextString())
+ }
+ }
+
+ @Test
+ fun testIgnoresTagsOnNumbers() {
+ /*
+ * 86 # array(6)
+ * 18 7B # unsigned(123)
+ * C0 # tag(0)
+ * 1A 0001E240 # unsigned(123456)
+ * D8 F5 # tag(245)
+ * 1A 000F423F # unsigned(999999)
+ * D9 3039 # tag(12345)
+ * 38 31 # negative(49)
+ * D8 22 # tag(34)
+ * FB 3FE161F9F01B866E # primitive(4603068020252444270)
+ * D9 0237 # tag(567)
+ * FB 401999999999999A # primitive(4618891777831180698)
+ */
+ withDecoder("86187BC01A0001E240D8F51A000F423FD930393831D822FB3FE161F9F01B866ED90237FB401999999999999A") {
+ assertEquals(6, startArray())
+ assertEquals(123, nextNumber())
+ assertEquals(123456, nextNumber())
+ assertEquals(999999, nextNumber())
+ assertEquals(-50, nextNumber())
+ assertEquals(0.54321, nextDouble(), 0.00001)
+ assertEquals(6.4, nextDouble(), 0.00001)
+ }
+ }
+
+ @Test
+ fun testIgnoresTagsOnArraysAndMaps() {
+ /*
+ * A2 # map(2)
+ * 63 # text(3)
+ * 6D6170 # "map"
+ * D8 7B # tag(123)
+ * A1 # map(1)
+ * 68 # text(8)
+ * 74686973206D6170 # "this map"
+ * 6D # text(13)
+ * 69732074616767656420313233 # "is tagged 123"
+ * 65 # text(5)
+ * 6172726179 # "array"
+ * DA 0012D687 # tag(1234567)
+ * 83 # array(3)
+ * 6A # text(10)
+ * 74686973206172726179 # "this array"
+ * 69 # text(9)
+ * 697320746167676564 # "is tagged"
+ * 67 # text(7)
+ * 31323334353637 # "1234567"
+ */
+ withDecoder("A2636D6170D87BA16874686973206D61706D69732074616767656420313233656172726179DA0012D687836A74686973206172726179696973207461676765646731323334353637") {
+ assertEquals(2, startMap())
+ assertEquals("map", nextString())
+ assertEquals(1, startMap())
+ assertEquals("this map", nextString())
+ assertEquals("is tagged 123", nextString())
+ assertEquals("array", nextString())
+ assertEquals(3, startArray())
+ assertEquals("this array", nextString())
+ assertEquals("is tagged", nextString())
+ assertEquals("1234567", nextString())
+ }
+ }
+}
+
+private fun CborDecoder.expect(expected: String) {
+ assertEquals(expected, actual = nextString(), "string")
+}
+
+private fun CborDecoder.expectMap(size: Int) {
+ assertEquals(size, actual = startMap(), "map size")
+}
+
+private fun CborDecoder.expectEof() {
+ assertTrue(isEof(), "Expected EOF.")
+}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborRootLevelNullsTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborRootLevelNullsTest.kt
new file mode 100644
index 00000000..3e548348
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborRootLevelNullsTest.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlin.test.*
+
+class CborRootLevelNullsTest {
+ @Serializable
+ data class Simple(val a: Int = 42)
+
+ @Test
+ fun testNull() {
+ val obj: Simple? = null
+ val content = Cbor.encodeToByteArray(Simple.serializer().nullable, obj)
+ assertTrue(content.contentEquals(byteArrayOf(0xf6.toByte())))
+ }
+}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt
new file mode 100644
index 00000000..c546bdf6
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+
+class CbrWriterTest {
+ @Test
+ fun writeSimpleClass() {
+ assertEquals("bf616163737472ff", Cbor.encodeToHexString(Simple.serializer(), Simple("str")))
+ }
+
+ @Test
+ fun writeComplicatedClass() {
+ val test = TypesUmbrella(
+ "Hello, world!",
+ 42,
+ null,
+ listOf("a", "b"),
+ mapOf(1 to true, 2 to false),
+ Simple("lol"),
+ listOf(Simple("kek")),
+ HexConverter.parseHexBinary("cafe"),
+ HexConverter.parseHexBinary("cafe")
+ )
+ assertEquals(
+ "bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffff6a62797465537472696e6742cafe696279746541727261799f383521ffff",
+ Cbor.encodeToHexString(TypesUmbrella.serializer(), test)
+ )
+ }
+
+ @Test
+ fun writeManyNumbers() {
+ val test = NumberTypesUmbrella(
+ 100500,
+ Long.MAX_VALUE,
+ 42.0f,
+ 1235621356215.0,
+ true,
+ 'a'
+ )
+ assertEquals(
+ "bf63696e741a00018894646c6f6e671b7fffffffffffffff65666c6f6174fa4228000066646f75626c65fb4271fb0c5a2b700067626f6f6c65616ef564636861721861ff",
+ Cbor.encodeToHexString(NumberTypesUmbrella.serializer(), test)
+ )
+ }
+
+ @Test
+ fun testWriteByteStringWhenNullable() {
+ /* BF # map(*)
+ * 6A # text(10)
+ * 62797465537472696E67 # "byteString"
+ * 44 # bytes(4)
+ * 01020304 # "\x01\x02\x03\x04"
+ * FF # primitive(*)
+ */
+ assertEquals(
+ expected = "bf6a62797465537472696e674401020304ff",
+ actual = Cbor.encodeToHexString(
+ serializer = NullableByteString.serializer(),
+ value = NullableByteString(byteString = byteArrayOf(1, 2, 3, 4))
+ )
+ )
+
+ /* BF # map(*)
+ * 6A # text(10)
+ * 62797465537472696E67 # "byteString"
+ * 40 # bytes(0)
+ * # ""
+ * FF # primitive(*)
+ */
+ assertEquals(
+ expected = "bf6a62797465537472696e6740ff",
+ actual = Cbor.encodeToHexString(
+ serializer = NullableByteString.serializer(),
+ value = NullableByteString(byteString = byteArrayOf())
+ )
+ )
+ }
+
+ @Test
+ fun testWriteNullForNullableByteString() {
+ /* BF # map(*)
+ * 6A # text(10)
+ * 62797465537472696E67 # "byteString"
+ * F6 # primitive(22)
+ * FF # primitive(*)
+ */
+ assertEquals(
+ expected = "bf6a62797465537472696e67f6ff",
+ actual = Cbor.encodeToHexString(
+ serializer = NullableByteString.serializer(),
+ value = NullableByteString(byteString = null)
+ )
+ )
+ }
+
+ @Test
+ fun testWriteCustomByteString() {
+ assertEquals(
+ expected = "bf617843112233ff",
+ actual = Cbor.encodeToHexString(TypeWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)))
+ )
+ }
+
+ @Test
+ fun testWriteNullableCustomByteString() {
+ assertEquals(
+ expected = "bf617843112233ff",
+ actual = Cbor.encodeToHexString(TypeWithNullableCustomByteString(CustomByteString(0x11, 0x22, 0x33)))
+ )
+ }
+
+ @Test
+ fun testWriteNullCustomByteString() {
+ assertEquals(
+ expected = "bf6178f6ff",
+ actual = Cbor.encodeToHexString(TypeWithNullableCustomByteString(null))
+ )
+ }
+}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt
new file mode 100644
index 00000000..ad55d042
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+@Serializable
+data class Simple(val a: String)
+
+@Serializable
+data class TypesUmbrella(
+ val str: String,
+ val i: Int,
+ val nullable: Double?,
+ val list: List<String>,
+ val map: Map<Int, Boolean>,
+ val inner: Simple,
+ val innersList: List<Simple>,
+ @ByteString val byteString: ByteArray,
+ val byteArray: ByteArray
+) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as TypesUmbrella
+
+ if (str != other.str) return false
+ if (i != other.i) return false
+ if (nullable != other.nullable) return false
+ if (list != other.list) return false
+ if (map != other.map) return false
+ if (inner != other.inner) return false
+ if (innersList != other.innersList) return false
+ if (!byteString.contentEquals(other.byteString)) return false
+ if (!byteArray.contentEquals(other.byteArray)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = str.hashCode()
+ result = 31 * result + i
+ result = 31 * result + (nullable?.hashCode() ?: 0)
+ result = 31 * result + list.hashCode()
+ result = 31 * result + map.hashCode()
+ result = 31 * result + inner.hashCode()
+ result = 31 * result + innersList.hashCode()
+ result = 31 * result + byteString.contentHashCode()
+ result = 31 * result + byteArray.contentHashCode()
+ return result
+ }
+}
+
+@Serializable
+data class NumberTypesUmbrella(
+ val int: Int,
+ val long: Long,
+ val float: Float,
+ val double: Double,
+ val boolean: Boolean,
+ val char: Char
+)
+
+@Serializable
+data class NullableByteString(
+ @ByteString val byteString: ByteArray?
+) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as NullableByteString
+
+ if (byteString != null) {
+ if (other.byteString == null) return false
+ if (!byteString.contentEquals(other.byteString)) return false
+ } else if (other.byteString != null) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ return byteString?.contentHashCode() ?: 0
+ }
+}
+
+@Serializable(with = CustomByteStringSerializer::class)
+data class CustomByteString(val a: Byte, val b: Byte, val c: Byte)
+
+class CustomByteStringSerializer : KSerializer<CustomByteString> {
+ override val descriptor = SerialDescriptor("CustomByteString", ByteArraySerializer().descriptor)
+
+ override fun serialize(encoder: Encoder, value: CustomByteString) {
+ encoder.encodeSerializableValue(ByteArraySerializer(), byteArrayOf(value.a, value.b, value.c))
+ }
+
+ override fun deserialize(decoder: Decoder): CustomByteString {
+ val array = decoder.decodeSerializableValue(ByteArraySerializer())
+ return CustomByteString(array[0], array[1], array[2])
+ }
+}
+
+@Serializable
+data class TypeWithCustomByteString(@ByteString val x: CustomByteString)
+
+@Serializable
+data class TypeWithNullableCustomByteString(@ByteString val x: CustomByteString?) \ No newline at end of file
diff --git a/formats/cbor/jvmMainModule/src/module-info.java b/formats/cbor/jvmMainModule/src/module-info.java
new file mode 100644
index 00000000..5ddd0ac3
--- /dev/null
+++ b/formats/cbor/jvmMainModule/src/module-info.java
@@ -0,0 +1,6 @@
+module kotlinx.serialization.cbor {
+ requires transitive kotlin.stdlib;
+ requires transitive kotlinx.serialization.core;
+
+ exports kotlinx.serialization.cbor;
+}
diff --git a/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborCompatibilityTest.kt b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborCompatibilityTest.kt
new file mode 100644
index 00000000..85b82b1e
--- /dev/null
+++ b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborCompatibilityTest.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import com.upokecenter.cbor.*
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import org.junit.Test
+import kotlin.test.*
+
+class CborCompatibilityTest {
+
+ @Serializable
+ data class SomeClass(val prop: Int = 0)
+
+ @Serializable
+ data class WithMap(val map: Map<Long, Long>)
+ @Serializable
+ data class IntData(val intV: Int)
+ @Serializable
+ data class StringData(val data: String)
+ @Serializable
+ data class FloatData(val field: Float)
+ @Serializable
+ data class DoubleData(val field: Double)
+
+ @Serializable
+ data class SomeComplexClass<T>(
+ val boxed: T,
+ val otherClass: StringData,
+ val primitive: Int,
+ val map: Map<String, IntData>
+ )
+
+ private inline fun <reified T> compare(obj: T, serializer: KSerializer<T>) {
+ val bytes = CBORObject.FromObject(obj).EncodeToBytes()
+ assertEquals(obj, Cbor.decodeFromByteArray(serializer, bytes))
+ }
+
+ private fun compareDouble(value: Double) {
+ val doubleWrapper = DoubleData(value)
+ val bytes = CBORObject.FromObject(doubleWrapper).EncodeToBytes()
+ assertEquals(doubleWrapper, Cbor.decodeFromByteArray(DoubleData.serializer(), bytes))
+ }
+
+ private fun compareFloat(value: Float) {
+ val floatWrapper = FloatData(value)
+ val bytes = CBORObject.FromObject(floatWrapper).EncodeToBytes()
+ assertEquals(floatWrapper, Cbor.decodeFromByteArray(FloatData.serializer(), bytes))
+ }
+
+ @Test
+ fun basicClassFromAnotherLibrary() {
+ compare(SomeClass(), SomeClass.serializer())
+ }
+
+ @Test
+ fun basicListFromAnotherLibrary() {
+ compare(
+ listOf(
+ SomeClass(1),
+ SomeClass(2),
+ SomeClass(3)
+ ), ListSerializer(SomeClass.serializer())
+ )
+ }
+
+ @Test
+ fun withMap() {
+ compare(WithMap(mapOf()), WithMap.serializer())
+ compare(WithMap(mapOf(10L to 10L)), WithMap.serializer())
+ compare(
+ WithMap(
+ mapOf(
+ 10L to 10L,
+ 20L to 20L
+ )
+ ), WithMap.serializer())
+ }
+
+ @Test
+ fun someComplexClass() {
+ val obj = SomeComplexClass(
+ listOf(10),
+ StringData("20"),
+ 30,
+ mapOf("40" to IntData(40), "50" to IntData(50))
+ )
+ val serial = SomeComplexClass.serializer(ListSerializer(Int.serializer()))
+ compare(obj, serial)
+ }
+
+ @Test
+ fun testFloat() {
+ compareFloat(Float.NaN)
+ compareFloat(Float.POSITIVE_INFINITY)
+ compareFloat(Float.NEGATIVE_INFINITY)
+ compareFloat(Float.MAX_VALUE)
+ compareFloat(Float.MIN_VALUE)
+ compareFloat(0.0f)
+ compareFloat(-0.0f)
+ compareFloat(-1.0f)
+ compareFloat(1.0f)
+ compareFloat(123.56f)
+ compareFloat(123.0f)
+ // minimal denormalized value in half-precision
+ compareFloat(5.9604645E-8f)
+ // maximal denormalized value in half-precision
+ compareFloat(0.000060975552f)
+ }
+
+ @Test
+ fun testDouble() {
+ compareDouble(Double.NaN)
+ compareDouble(Double.POSITIVE_INFINITY)
+ compareDouble(Double.NEGATIVE_INFINITY)
+ compareDouble(Double.MAX_VALUE)
+ compareDouble(Double.MIN_VALUE)
+ compareDouble(0.0)
+ compareDouble(-0.0)
+ compareDouble(-1.0)
+ compareDouble(1.0)
+ compareDouble(123.56)
+ compareDouble(123.0)
+ // minimal denormalized value in half-precision
+ compareDouble(5.9604644775390625E-8)
+ // maximal denormalized value in half-precision
+ compareDouble(0.00006097555160522461)
+ }
+}
diff --git a/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborStacktraceRecoveryTest.kt b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborStacktraceRecoveryTest.kt
new file mode 100644
index 00000000..98ebc49b
--- /dev/null
+++ b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborStacktraceRecoveryTest.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.coroutines.*
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.internal.*
+import kotlin.test.*
+
+class CborStacktraceRecoveryTest {
+ @Test
+ fun testCborDecodingException() = checkRecovered<CborDecodingException> {
+ Cbor.decodeFromByteArray<String>(byteArrayOf(0xFF.toByte()))
+ }
+
+ private inline fun <reified E : Exception> checkRecovered(noinline block: () -> Unit) = runBlocking {
+ val result = runCatching {
+ callBlockWithRecovery(block)
+ }
+ assertTrue(result.isFailure, "Block should have failed")
+ val e = result.exceptionOrNull()!!
+ assertEquals(E::class, e::class)
+ val cause = e.cause
+ assertNotNull(cause, "Exception should have cause: $e")
+ assertEquals(e.message, cause.message)
+ assertEquals(E::class, cause::class)
+ }
+
+ // KLUDGE: A separate function with state-machine to ensure coroutine DebugMetadata is generated. See KT-41789
+ private suspend fun callBlockWithRecovery(block: () -> Unit) {
+ yield()
+ // use withContext to perform switch between coroutines and thus trigger exception recovery machinery
+ withContext(NonCancellable) {
+ block()
+ }
+ }
+}
diff --git a/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborWriterSpecTest.kt b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborWriterSpecTest.kt
new file mode 100644
index 00000000..364cd675
--- /dev/null
+++ b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborWriterSpecTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import io.kotlintest.matchers.*
+import io.kotlintest.properties.*
+import io.kotlintest.specs.*
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.internal.*
+
+class CborWriterSpecTest : WordSpec() {
+ init {
+
+ fun withEncoder(block: CborEncoder.() -> Unit): String {
+ val result = ByteArrayOutput()
+ CborEncoder(result).block()
+ return HexConverter.printHexBinary(result.toByteArray()).lowercase()
+ }
+
+ // Examples from https://tools.ietf.org/html/rfc7049#appendix-A
+ "CBOR Encoder" should {
+ "encode integers" {
+ val tabl = table(
+ headers("input", "output"),
+ row(0, "00"),
+ row(10, "0a"),
+ row(25, "1819"),
+ row(1000, "1903e8"),
+ row(-1, "20"),
+ row(-1000, "3903e7")
+ )
+ forAll(tabl) { input, output ->
+ withEncoder { encodeNumber(input.toLong()) } shouldBe output
+ }
+ }
+
+ "encode doubles" {
+ val tabl = table(
+ headers("input", "output"),
+ row(1.0e+300, "fb7e37e43c8800759c"),
+ row(-4.1, "fbc010666666666666")
+ )
+ forAll(tabl) { input, output ->
+ withEncoder { encodeDouble(input) } shouldBe output
+ }
+ }
+
+ "encode strings" {
+ val tabl = table(
+ headers("input", "output"),
+ row("IETF", "6449455446"),
+ row("\"\\", "62225c"),
+ row("\ud800\udd51", "64f0908591")
+ )
+ forAll(tabl) { input, output ->
+ withEncoder { encodeString(input) } shouldBe output
+ }
+ }
+ }
+ }
+}
diff --git a/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/RandomTests.kt b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/RandomTests.kt
new file mode 100644
index 00000000..7a11fc5e
--- /dev/null
+++ b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/RandomTests.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import io.kotlintest.properties.*
+import io.kotlintest.specs.*
+import kotlinx.serialization.*
+
+class RandomTest : ShouldSpec() {
+
+ companion object {
+ fun Gen<String>.generateNotEmpty() = nextPrintableString(Gen.choose(1, 100).generate())
+ }
+
+ object KTestData {
+ @Serializable
+ data class KTestInt32(val a: Int) {
+ companion object : Gen<KTestInt32> {
+ override fun generate(): KTestInt32 = KTestInt32(Gen.int().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestSignedInt(val a: Int) {
+ companion object : Gen<KTestSignedInt> {
+ override fun generate(): KTestSignedInt = KTestSignedInt(Gen.int().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestSignedLong(val a: Long) {
+ companion object : Gen<KTestSignedLong> {
+ override fun generate(): KTestSignedLong = KTestSignedLong(Gen.long().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestFixedInt(val a: Int) {
+ companion object : Gen<KTestFixedInt> {
+ override fun generate(): KTestFixedInt = KTestFixedInt(Gen.int().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestDouble(val a: Double) {
+ companion object : Gen<KTestDouble> {
+ override fun generate(): KTestDouble = KTestDouble(Gen.double().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestBoolean(val a: Boolean) {
+ companion object : Gen<KTestBoolean> {
+ override fun generate(): KTestBoolean = KTestBoolean(Gen.bool().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestAllTypes(
+ val i32: Int,
+ val si32: Int,
+ val f32: Int,
+ val i64: Long,
+ val si64: Long,
+ val f64: Long,
+ val f: Float,
+ val d: Double,
+ val b: Boolean = false,
+ val s: String
+ ) {
+
+ companion object : Gen<KTestAllTypes> {
+ override fun generate(): KTestAllTypes = KTestAllTypes(
+ Gen.int().generate(),
+ Gen.int().generate(),
+ Gen.int().generate(),
+ Gen.long().generate(),
+ Gen.long().generate(),
+ Gen.long().generate(),
+ Gen.float().generate(),
+ Gen.double().generate(),
+ Gen.bool().generate(),
+ Gen.string().generateNotEmpty()
+ )
+ }
+ }
+
+ @Serializable
+ data class KTestOuterMessage(
+ val a: Int,
+ val b: Double,
+ val inner: KTestAllTypes,
+ val s: String
+ ) {
+ companion object : Gen<KTestOuterMessage> {
+ override fun generate(): KTestOuterMessage = KTestOuterMessage(
+ Gen.int().generate(),
+ Gen.double().generate(),
+ KTestAllTypes.generate(),
+ Gen.string().generateNotEmpty()
+ )
+ }
+ }
+
+ @Serializable
+ data class KTestIntListMessage(
+ val s: Int,
+ val l: List<Int>
+ ) {
+ companion object : Gen<KTestIntListMessage> {
+ override fun generate() = KTestIntListMessage(Gen.int().generate(), Gen.list(Gen.int()).generate())
+ }
+ }
+
+ @Serializable
+ data class KTestObjectListMessage(
+ val inner: List<KTestAllTypes>
+ ) {
+ companion object : Gen<KTestObjectListMessage> {
+ override fun generate() = KTestObjectListMessage(Gen.list(KTestAllTypes.Companion).generate())
+ }
+ }
+
+ enum class KCoffee { AMERICANO, LATTE, CAPPUCCINO }
+
+ @Serializable
+ data class KTestEnum(val a: KCoffee) {
+ companion object : Gen<KTestEnum> {
+ override fun generate(): KTestEnum = KTestEnum(Gen.oneOf<KCoffee>().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestMap(val s: Map<String, String>, val o: Map<Int, KTestAllTypes> = emptyMap()) {
+ companion object : Gen<KTestMap> {
+ override fun generate(): KTestMap =
+ KTestMap(Gen.map(Gen.string(), Gen.string()).generate(), Gen.map(Gen.int(), KTestAllTypes).generate())
+ }
+ }
+ }
+
+ init {
+ "CBOR Writer" {
+ should("serialize random int32") { forAll(KTestData.KTestInt32.Companion) { dumpCborCompare(it) } }
+ should("serialize random signed int32") { forAll(KTestData.KTestSignedInt.Companion) { dumpCborCompare(it) } }
+ should("serialize random signed int64") { forAll(KTestData.KTestSignedLong.Companion) { dumpCborCompare(it) } }
+ should("serialize random fixed int32") { forAll(KTestData.KTestFixedInt.Companion) { dumpCborCompare(it) } }
+ should("serialize random doubles") { forAll(KTestData.KTestDouble.Companion) { dumpCborCompare(it) } }
+ should("serialize random booleans") { forAll(KTestData.KTestBoolean.Companion) { dumpCborCompare(it) } }
+ should("serialize random enums") { forAll(KTestData.KTestEnum.Companion) { dumpCborCompare(it) } }
+ should("serialize all base random types") { forAll(KTestData.KTestAllTypes.Companion) { dumpCborCompare(it) } }
+ should("serialize random messages with embedded message") {
+ forAll(KTestData.KTestOuterMessage.Companion) {
+ dumpCborCompare(
+ it
+ )
+ }
+ }
+ should("serialize random messages with primitive list fields") {
+ forAll(KTestData.KTestIntListMessage.Companion) {
+ dumpCborCompare(
+ it
+ )
+ }
+ }
+ should("serialize messages with object list fields") {
+ forAll(KTestData.KTestObjectListMessage.Companion) {
+ dumpCborCompare(
+ it
+ )
+ }
+ }
+ should("serialize messages with scalar-key maps") {
+ forAll(KTestData.KTestMap.Companion) {
+ dumpCborCompare(
+ it
+ )
+ }
+ }
+ }
+
+ "CBOR Reader" {
+ should("read random int32") { forAll(KTestData.KTestInt32.Companion) { readCborCompare(it) } }
+ should("read random signed int32") { forAll(KTestData.KTestSignedInt.Companion) { readCborCompare(it) } }
+ should("read random signed int64") { forAll(KTestData.KTestSignedLong.Companion) { readCborCompare(it) } }
+ should("read random fixed int32") { forAll(KTestData.KTestFixedInt.Companion) { readCborCompare(it) } }
+ should("read random doubles") { forAll(KTestData.KTestDouble.Companion) { readCborCompare(it) } }
+ should("read random enums") { forAll(KTestData.KTestEnum.Companion) { readCborCompare(it) } }
+ should("read all base random types") { forAll(KTestData.KTestAllTypes.Companion) { readCborCompare(it) } }
+ should("read random messages with embedded message") {
+ forAll(KTestData.KTestOuterMessage.Companion) {
+ readCborCompare(
+ it
+ )
+ }
+ }
+ should("read random messages with primitive list fields") {
+ forAll(KTestData.KTestIntListMessage.Companion) {
+ readCborCompare(
+ it
+ )
+ }
+ }
+ should("read random messages with object list fields") {
+ forAll(KTestData.KTestObjectListMessage.Companion) {
+ readCborCompare(
+ it
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/TestUtilities.kt b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/TestUtilities.kt
new file mode 100644
index 00000000..052964c7
--- /dev/null
+++ b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/TestUtilities.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import com.fasterxml.jackson.databind.*
+import com.fasterxml.jackson.dataformat.cbor.*
+import com.fasterxml.jackson.module.kotlin.*
+import kotlinx.serialization.*
+
+internal val cborJackson = ObjectMapper(CBORFactory()).apply { registerKotlinModule() }
+internal val defaultCbor = Cbor { encodeDefaults = true }
+
+internal inline fun <reified T : Any> dumpCborCompare(it: T, alwaysPrint: Boolean = false): Boolean {
+ var parsed: T?
+ val c = try {
+ val bytes = defaultCbor.encodeToByteArray(it)
+ parsed = cborJackson.readValue<T>(bytes)
+ it == parsed
+ } catch (e: Exception) {
+ e.printStackTrace()
+ parsed = null
+ false
+ }
+ if (!c || alwaysPrint) println("Expected: $it\nfound: $parsed")
+ return c
+}
+
+internal inline fun <reified T: Any> readCborCompare(it: T, alwaysPrint: Boolean = false): Boolean {
+ var obj: T?
+ val c = try {
+ val hex = cborJackson.writeValueAsBytes(it)
+ obj = defaultCbor.decodeFromByteArray(hex)
+ obj == it
+ } catch (e: Exception) {
+ obj = null
+ e.printStackTrace()
+ false
+ }
+ if (!c || alwaysPrint) println("Expected: $it\nfound: $obj")
+ return c
+}
diff --git a/formats/hocon/api/kotlinx-serialization-hocon.api b/formats/hocon/api/kotlinx-serialization-hocon.api
new file mode 100644
index 00000000..a29292d0
--- /dev/null
+++ b/formats/hocon/api/kotlinx-serialization-hocon.api
@@ -0,0 +1,29 @@
+public abstract class kotlinx/serialization/hocon/Hocon : kotlinx/serialization/SerialFormat {
+ public static final field Default Lkotlinx/serialization/hocon/Hocon$Default;
+ public synthetic fun <init> (ZZZLjava/lang/String;Lkotlinx/serialization/modules/SerializersModule;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun decodeFromConfig (Lkotlinx/serialization/DeserializationStrategy;Lcom/typesafe/config/Config;)Ljava/lang/Object;
+ public final fun encodeToConfig (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Lcom/typesafe/config/Config;
+ public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public final class kotlinx/serialization/hocon/Hocon$Default : kotlinx/serialization/hocon/Hocon {
+}
+
+public final class kotlinx/serialization/hocon/HoconBuilder {
+ public final fun getClassDiscriminator ()Ljava/lang/String;
+ public final fun getEncodeDefaults ()Z
+ public final fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+ public final fun getUseArrayPolymorphism ()Z
+ public final fun getUseConfigNamingConvention ()Z
+ public final fun setClassDiscriminator (Ljava/lang/String;)V
+ public final fun setEncodeDefaults (Z)V
+ public final fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V
+ public final fun setUseArrayPolymorphism (Z)V
+ public final fun setUseConfigNamingConvention (Z)V
+}
+
+public final class kotlinx/serialization/hocon/HoconKt {
+ public static final fun Hocon (Lkotlinx/serialization/hocon/Hocon;Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/hocon/Hocon;
+ public static synthetic fun Hocon$default (Lkotlinx/serialization/hocon/Hocon;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/serialization/hocon/Hocon;
+}
+
diff --git a/formats/hocon/build.gradle b/formats/hocon/build.gradle
new file mode 100644
index 00000000..0da4b083
--- /dev/null
+++ b/formats/hocon/build.gradle
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+apply plugin: 'kotlin'
+apply plugin: 'kotlinx-serialization'
+
+compileKotlin {
+ kotlinOptions {
+ allWarningsAsErrors = true
+ jvmTarget = '1.8'
+ }
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+
+dependencies {
+ implementation project(':kotlinx-serialization-core')
+ api 'org.jetbrains.kotlin:kotlin-stdlib'
+
+ api 'com.typesafe:config:1.4.1'
+
+ testImplementation 'org.jetbrains.kotlin:kotlin-test'
+ testImplementation 'junit:junit:4.12'
+}
+
+Java9Modularity.configureJava9ModuleInfo(project)
diff --git a/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/Hocon.kt b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/Hocon.kt
new file mode 100644
index 00000000..e8728352
--- /dev/null
+++ b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/Hocon.kt
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.hocon
+
+import com.typesafe.config.*
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.encoding.CompositeDecoder.Companion.DECODE_DONE
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.modules.*
+
+/**
+ * Allows [deserialization][decodeFromConfig]
+ * of [Config] object from popular Lightbend/config library into Kotlin objects.
+ *
+ * [Config] object represents "Human-Optimized Config Object Notation" —
+ * [HOCON][https://github.com/lightbend/config#using-hocon-the-json-superset].
+ *
+ * @param [useConfigNamingConvention] switches naming resolution to config naming convention (hyphen separated).
+ * @param serializersModule A [SerializersModule] which should contain registered serializers
+ * for [Contextual] and [Polymorphic] serialization, if you have any.
+ */
+@ExperimentalSerializationApi
+public sealed class Hocon(
+ internal val encodeDefaults: Boolean,
+ internal val useConfigNamingConvention: Boolean,
+ internal val useArrayPolymorphism: Boolean,
+ internal val classDiscriminator: String,
+ override val serializersModule: SerializersModule,
+) : SerialFormat {
+
+ /**
+ * Decodes the given [config] into a value of type [T] using the given serializer.
+ */
+ @ExperimentalSerializationApi
+ public fun <T> decodeFromConfig(deserializer: DeserializationStrategy<T>, config: Config): T =
+ ConfigReader(config).decodeSerializableValue(deserializer)
+
+ /**
+ * Encodes the given [value] into a [Config] using the given [serializer].
+ * @throws SerializationException If list or primitive type passed as a [value].
+ */
+ @ExperimentalSerializationApi
+ public fun <T> encodeToConfig(serializer: SerializationStrategy<T>, value: T): Config {
+ lateinit var configValue: ConfigValue
+ val encoder = HoconConfigEncoder(this) { configValue = it }
+ encoder.encodeSerializableValue(serializer, value)
+
+ if (configValue !is ConfigObject) {
+ throw SerializationException(
+ "Value of type '${configValue.valueType()}' can't be used at the root of HOCON Config. " +
+ "It should be either object or map."
+ )
+ }
+ return (configValue as ConfigObject).toConfig()
+ }
+
+ /**
+ * The default instance of Hocon parser.
+ */
+ @ExperimentalSerializationApi
+ public companion object Default : Hocon(false, false, false, "type", EmptySerializersModule)
+
+ private abstract inner class ConfigConverter<T> : TaggedDecoder<T>() {
+ override val serializersModule: SerializersModule
+ get() = this@Hocon.serializersModule
+
+ abstract fun <E> getValueFromTaggedConfig(tag: T, valueResolver: (Config, String) -> E): E
+
+ private inline fun <reified E : Any> validateAndCast(tag: T): E {
+ return try {
+ when (E::class) {
+ Number::class -> getValueFromTaggedConfig(tag) { config, path -> config.getNumber(path) } as E
+ Boolean::class -> getValueFromTaggedConfig(tag) { config, path -> config.getBoolean(path) } as E
+ String::class -> getValueFromTaggedConfig(tag) { config, path -> config.getString(path) } as E
+ else -> getValueFromTaggedConfig(tag) { config, path -> config.getAnyRef(path) } as E
+ }
+ } catch (e: ConfigException) {
+ val configOrigin = e.origin()
+ throw ConfigValueTypeCastException<E>(configOrigin)
+ }
+ }
+
+ private fun getTaggedNumber(tag: T) = validateAndCast<Number>(tag)
+
+ override fun decodeTaggedString(tag: T) = validateAndCast<String>(tag)
+
+ override fun decodeTaggedBoolean(tag: T) = validateAndCast<Boolean>(tag)
+ override fun decodeTaggedByte(tag: T): Byte = getTaggedNumber(tag).toByte()
+ override fun decodeTaggedShort(tag: T): Short = getTaggedNumber(tag).toShort()
+ override fun decodeTaggedInt(tag: T): Int = getTaggedNumber(tag).toInt()
+ override fun decodeTaggedLong(tag: T): Long = getTaggedNumber(tag).toLong()
+ override fun decodeTaggedFloat(tag: T): Float = getTaggedNumber(tag).toFloat()
+ override fun decodeTaggedDouble(tag: T): Double = getTaggedNumber(tag).toDouble()
+
+ override fun decodeTaggedChar(tag: T): Char {
+ val s = validateAndCast<String>(tag)
+ if (s.length != 1) throw SerializationException("String \"$s\" is not convertible to Char")
+ return s[0]
+ }
+
+ override fun decodeTaggedValue(tag: T): Any = getValueFromTaggedConfig(tag) { c, s -> c.getAnyRef(s) }
+
+ override fun decodeTaggedNotNullMark(tag: T) = getValueFromTaggedConfig(tag) { c, s -> !c.getIsNull(s) }
+
+ override fun decodeTaggedEnum(tag: T, enumDescriptor: SerialDescriptor): Int {
+ val s = validateAndCast<String>(tag)
+ return enumDescriptor.getElementIndexOrThrow(s)
+ }
+ }
+
+ private inner class ConfigReader(val conf: Config) : ConfigConverter<String>() {
+ private var ind = -1
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ while (++ind < descriptor.elementsCount) {
+ val name = descriptor.getTag(ind)
+ if (conf.hasPathOrNull(name)) {
+ return ind
+ }
+ }
+ return DECODE_DONE
+ }
+
+ private fun composeName(parentName: String, childName: String) =
+ if (parentName.isEmpty()) childName else "$parentName.$childName"
+
+ override fun SerialDescriptor.getTag(index: Int): String =
+ composeName(currentTagOrNull.orEmpty(), getConventionElementName(index, useConfigNamingConvention))
+
+ override fun decodeNotNullMark(): Boolean {
+ // Tag might be null for top-level deserialization
+ val currentTag = currentTagOrNull ?: return !conf.isEmpty
+ return decodeTaggedNotNullMark(currentTag)
+ }
+
+ override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
+ if (deserializer !is AbstractPolymorphicSerializer<*> || useArrayPolymorphism) {
+ return deserializer.deserialize(this)
+ }
+
+ val config = if (currentTagOrNull != null) conf.getConfig(currentTag) else conf
+
+ val reader = ConfigReader(config)
+ val type = reader.decodeTaggedString(classDiscriminator)
+ val actualSerializer = deserializer.findPolymorphicSerializerOrNull(reader, type)
+ ?: throw SerializerNotFoundException(type)
+
+ @Suppress("UNCHECKED_CAST")
+ return (actualSerializer as DeserializationStrategy<T>).deserialize(reader)
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ val kind = descriptor.hoconKind(useArrayPolymorphism)
+
+ return when {
+ kind.listLike -> ListConfigReader(conf.getList(currentTag))
+ kind.objLike -> if (ind > -1) ConfigReader(conf.getConfig(currentTag)) else this
+ kind == StructureKind.MAP ->
+ // if current tag is null - map in the root of config
+ MapConfigReader(if (currentTagOrNull != null) conf.getObject(currentTag) else conf.root())
+ else -> this
+ }
+ }
+
+ override fun <E> getValueFromTaggedConfig(tag: String, valueResolver: (Config, String) -> E): E {
+ return valueResolver(conf, tag)
+ }
+ }
+
+ private inner class ListConfigReader(private val list: ConfigList) : ConfigConverter<Int>() {
+ private var ind = -1
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ when {
+ descriptor.kind.listLike -> ListConfigReader(list[currentTag] as ConfigList)
+ descriptor.kind.objLike -> ConfigReader((list[currentTag] as ConfigObject).toConfig())
+ descriptor.kind == StructureKind.MAP -> MapConfigReader(list[currentTag] as ConfigObject)
+ else -> this
+ }
+
+ override fun SerialDescriptor.getTag(index: Int) = index
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ ind++
+ return if (ind > list.size - 1) DECODE_DONE else ind
+ }
+
+ override fun <E> getValueFromTaggedConfig(tag: Int, valueResolver: (Config, String) -> E): E {
+ val tagString = tag.toString()
+ val configValue = valueResolver(list[tag].atKey(tagString), tagString)
+ return configValue
+ }
+ }
+
+ private inner class MapConfigReader(map: ConfigObject) : ConfigConverter<Int>() {
+ private var ind = -1
+ private val keys: List<String>
+ private val values: List<ConfigValue>
+
+ init {
+ val entries = map.entries.toList() // to fix traversal order
+ keys = entries.map(MutableMap.MutableEntry<String, ConfigValue>::key)
+ values = entries.map(MutableMap.MutableEntry<String, ConfigValue>::value)
+ }
+
+ private val indexSize = values.size * 2
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ when {
+ descriptor.kind.listLike -> ListConfigReader(values[currentTag / 2] as ConfigList)
+ descriptor.kind.objLike -> ConfigReader((values[currentTag / 2] as ConfigObject).toConfig())
+ descriptor.kind == StructureKind.MAP -> MapConfigReader(values[currentTag / 2] as ConfigObject)
+ else -> this
+ }
+
+ override fun SerialDescriptor.getTag(index: Int) = index
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ ind++
+ return if (ind >= indexSize) DECODE_DONE else ind
+ }
+
+ override fun <E> getValueFromTaggedConfig(tag: Int, valueResolver: (Config, String) -> E): E {
+ val idx = tag / 2
+ val tagString = tag.toString()
+ val configValue = if (tag % 2 == 0) { // entry as string
+ ConfigValueFactory.fromAnyRef(keys[idx]).atKey(tagString)
+ } else {
+ val configValue = values[idx]
+ configValue.atKey(tagString)
+ }
+ return valueResolver(configValue, tagString)
+ }
+ }
+
+ private fun SerialDescriptor.getElementIndexOrThrow(name: String): Int {
+ val index = getElementIndex(name)
+ if (index == CompositeDecoder.UNKNOWN_NAME)
+ throw SerializationException("$serialName does not contain element with name '$name'")
+ return index
+ }
+}
+
+/**
+ * Decodes the given [config] into a value of type [T] using a deserializer retrieved
+ * from the reified type parameter.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Hocon.decodeFromConfig(config: Config): T =
+ decodeFromConfig(serializersModule.serializer(), config)
+
+/**
+ * Encodes the given [value] of type [T] into a [Config] using a serializer retrieved
+ * from the reified type parameter.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Hocon.encodeToConfig(value: T): Config =
+ encodeToConfig(serializersModule.serializer(), value)
+
+/**
+ * Creates an instance of [Hocon] configured from the optionally given [Hocon instance][from]
+ * and adjusted with [builderAction].
+ */
+@ExperimentalSerializationApi
+public fun Hocon(from: Hocon = Hocon, builderAction: HoconBuilder.() -> Unit): Hocon {
+ return HoconImpl(HoconBuilder(from).apply(builderAction))
+}
+
+/**
+ * Builder of the [Hocon] instance provided by `Hocon` factory function.
+ */
+@ExperimentalSerializationApi
+public class HoconBuilder internal constructor(hocon: Hocon) {
+ /**
+ * Module with contextual and polymorphic serializers to be used in the resulting [Hocon] instance.
+ */
+ public var serializersModule: SerializersModule = hocon.serializersModule
+
+ /**
+ * Specifies whether default values of Kotlin properties should be encoded.
+ * `false` by default.
+ */
+ public var encodeDefaults: Boolean = hocon.encodeDefaults
+
+ /**
+ * Switches naming resolution to config naming convention: hyphen separated.
+ */
+ public var useConfigNamingConvention: Boolean = hocon.useConfigNamingConvention
+
+ /**
+ * Switches polymorphic serialization to the default array format.
+ * This is an option for legacy polymorphism format and should not be generally used.
+ * `false` by default.
+ */
+ public var useArrayPolymorphism: Boolean = hocon.useArrayPolymorphism
+
+ /**
+ * Name of the class descriptor property for polymorphic serialization.
+ * "type" by default.
+ */
+ public var classDiscriminator: String = hocon.classDiscriminator
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private class HoconImpl(hoconBuilder: HoconBuilder) : Hocon(
+ encodeDefaults = hoconBuilder.encodeDefaults,
+ useConfigNamingConvention = hoconBuilder.useConfigNamingConvention,
+ useArrayPolymorphism = hoconBuilder.useArrayPolymorphism,
+ classDiscriminator = hoconBuilder.classDiscriminator,
+ serializersModule = hoconBuilder.serializersModule
+)
diff --git a/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconEncoder.kt b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconEncoder.kt
new file mode 100644
index 00000000..e7533198
--- /dev/null
+++ b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconEncoder.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.hocon
+
+import com.typesafe.config.*
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.modules.*
+
+@ExperimentalSerializationApi
+internal abstract class AbstractHoconEncoder(
+ private val hocon: Hocon,
+ private val valueConsumer: (ConfigValue) -> Unit,
+) : NamedValueEncoder() {
+
+ override val serializersModule: SerializersModule
+ get() = hocon.serializersModule
+
+ private var writeDiscriminator: Boolean = false
+
+ override fun elementName(descriptor: SerialDescriptor, index: Int): String {
+ return descriptor.getConventionElementName(index, hocon.useConfigNamingConvention)
+ }
+
+ override fun composeName(parentName: String, childName: String): String = childName
+
+ protected abstract fun encodeTaggedConfigValue(tag: String, value: ConfigValue)
+ protected abstract fun getCurrent(): ConfigValue
+
+ override fun encodeTaggedValue(tag: String, value: Any) = encodeTaggedConfigValue(tag, configValueOf(value))
+ override fun encodeTaggedNull(tag: String) = encodeTaggedConfigValue(tag, configValueOf(null))
+ override fun encodeTaggedChar(tag: String, value: Char) = encodeTaggedString(tag, value.toString())
+
+ override fun encodeTaggedEnum(tag: String, enumDescriptor: SerialDescriptor, ordinal: Int) {
+ encodeTaggedString(tag, enumDescriptor.getElementName(ordinal))
+ }
+
+ override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean = hocon.encodeDefaults
+
+ override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
+ if (serializer !is AbstractPolymorphicSerializer<*> || hocon.useArrayPolymorphism) {
+ serializer.serialize(this, value)
+ return
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ val casted = serializer as AbstractPolymorphicSerializer<Any>
+ val actualSerializer = casted.findPolymorphicSerializer(this, value as Any)
+ writeDiscriminator = true
+
+ actualSerializer.serialize(this, value)
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+ val consumer =
+ if (currentTagOrNull == null) valueConsumer
+ else { value -> encodeTaggedConfigValue(currentTag, value) }
+ val kind = descriptor.hoconKind(hocon.useArrayPolymorphism)
+
+ return when {
+ kind.listLike -> HoconConfigListEncoder(hocon, consumer)
+ kind.objLike -> HoconConfigEncoder(hocon, consumer)
+ kind == StructureKind.MAP -> HoconConfigMapEncoder(hocon, consumer)
+ else -> this
+ }.also { encoder ->
+ if (writeDiscriminator) {
+ encoder.encodeTaggedString(hocon.classDiscriminator, descriptor.serialName)
+ writeDiscriminator = false
+ }
+ }
+ }
+
+ override fun endEncode(descriptor: SerialDescriptor) {
+ valueConsumer(getCurrent())
+ }
+
+ private fun configValueOf(value: Any?) = ConfigValueFactory.fromAnyRef(value)
+}
+
+@ExperimentalSerializationApi
+internal class HoconConfigEncoder(hocon: Hocon, configConsumer: (ConfigValue) -> Unit) :
+ AbstractHoconEncoder(hocon, configConsumer) {
+
+ private val configMap = mutableMapOf<String, ConfigValue>()
+
+ override fun encodeTaggedConfigValue(tag: String, value: ConfigValue) {
+ configMap[tag] = value
+ }
+
+ override fun getCurrent(): ConfigValue = ConfigValueFactory.fromMap(configMap)
+}
+
+@ExperimentalSerializationApi
+internal class HoconConfigListEncoder(hocon: Hocon, configConsumer: (ConfigValue) -> Unit) :
+ AbstractHoconEncoder(hocon, configConsumer) {
+
+ private val values = mutableListOf<ConfigValue>()
+
+ override fun elementName(descriptor: SerialDescriptor, index: Int): String = index.toString()
+
+ override fun encodeTaggedConfigValue(tag: String, value: ConfigValue) {
+ values.add(tag.toInt(), value)
+ }
+
+ override fun getCurrent(): ConfigValue = ConfigValueFactory.fromIterable(values)
+}
+
+@ExperimentalSerializationApi
+internal class HoconConfigMapEncoder(hocon: Hocon, configConsumer: (ConfigValue) -> Unit) :
+ AbstractHoconEncoder(hocon, configConsumer) {
+
+ private val configMap = mutableMapOf<String, ConfigValue>()
+
+ private lateinit var key: String
+ private var isKey: Boolean = true
+
+ override fun encodeTaggedConfigValue(tag: String, value: ConfigValue) {
+ if (isKey) {
+ key = when (value.valueType()) {
+ ConfigValueType.OBJECT, ConfigValueType.LIST -> throw InvalidKeyKindException(value)
+ else -> value.unwrappedNullable().toString()
+ }
+ isKey = false
+ } else {
+ configMap[key] = value
+ isKey = true
+ }
+ }
+
+ override fun getCurrent(): ConfigValue = ConfigValueFactory.fromMap(configMap)
+
+ // Without cast to `Any?` Kotlin will assume unwrapped value as non-nullable by default
+ // and will call `Any.toString()` instead of extension-function `Any?.toString()`.
+ // We can't cast value in place using `(value.unwrapped() as Any?).toString()` because of warning "No cast needed".
+ private fun ConfigValue.unwrappedNullable(): Any? = unwrapped()
+}
diff --git a/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconExceptions.kt b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconExceptions.kt
new file mode 100644
index 00000000..52e711a1
--- /dev/null
+++ b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconExceptions.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.hocon
+
+import com.typesafe.config.*
+import kotlinx.serialization.*
+
+internal fun SerializerNotFoundException(type: String?) = SerializationException(
+ "Polymorphic serializer was not found for " +
+ if (type == null) "missing class discriminator ('null')" else "class discriminator '$type'"
+)
+
+internal inline fun <reified T> ConfigValueTypeCastException(valueOrigin: ConfigOrigin) = SerializationException(
+ "${valueOrigin.description()} required to be of type ${T::class.simpleName}."
+)
+
+internal fun InvalidKeyKindException(value: ConfigValue) = SerializationException(
+ "Value of type '${value.valueType()}' can't be used in HOCON as a key in the map. " +
+ "It should have either primitive or enum kind."
+)
diff --git a/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconSerialKind.kt b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconSerialKind.kt
new file mode 100644
index 00000000..c20d7de5
--- /dev/null
+++ b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconSerialKind.kt
@@ -0,0 +1,20 @@
+package kotlinx.serialization.hocon
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.hoconKind(useArrayPolymorphism: Boolean): SerialKind = when (kind) {
+ is PolymorphicKind -> {
+ if (useArrayPolymorphism) StructureKind.LIST else StructureKind.MAP
+ }
+ else -> kind
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal val SerialKind.listLike
+ get() = this == StructureKind.LIST || this is PolymorphicKind
+
+@OptIn(ExperimentalSerializationApi::class)
+internal val SerialKind.objLike
+ get() = this == StructureKind.CLASS || this == StructureKind.OBJECT
diff --git a/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/NamingConvention.kt b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/NamingConvention.kt
new file mode 100644
index 00000000..4071bc7b
--- /dev/null
+++ b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/NamingConvention.kt
@@ -0,0 +1,13 @@
+package kotlinx.serialization.hocon
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+
+private val NAMING_CONVENTION_REGEX by lazy { "[A-Z]".toRegex() }
+
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.getConventionElementName(index: Int, useConfigNamingConvention: Boolean): String {
+ val originalName = getElementName(index)
+ return if (!useConfigNamingConvention) originalName
+ else originalName.replace(NAMING_CONVENTION_REGEX) { "-${it.value.lowercase()}" }
+}
diff --git a/formats/hocon/src/mainModule/kotlin/module-info.java b/formats/hocon/src/mainModule/kotlin/module-info.java
new file mode 100644
index 00000000..b828065c
--- /dev/null
+++ b/formats/hocon/src/mainModule/kotlin/module-info.java
@@ -0,0 +1,7 @@
+module kotlinx.serialization.hocon {
+ requires transitive kotlin.stdlib;
+ requires transitive kotlinx.serialization.core;
+ requires transitive typesafe.config;
+
+ exports kotlinx.serialization.hocon;
+}
diff --git a/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconEncoderTest.kt b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconEncoderTest.kt
new file mode 100644
index 00000000..dbbe8682
--- /dev/null
+++ b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconEncoderTest.kt
@@ -0,0 +1,172 @@
+package kotlinx.serialization.hocon
+
+import kotlinx.serialization.*
+import org.junit.Test
+import kotlin.test.*
+
+class HoconEncoderTest {
+
+ @Serializable
+ data class SimpleConfig(val value: Int)
+
+ @Serializable
+ data class PrimitivesConfig(
+ val b: Boolean,
+ val i: Int,
+ val d: Double,
+ val c: Char,
+ val s: String,
+ val n: String?,
+ )
+
+ @Test
+ fun testEncodeSimpleConfig() {
+ val obj = PrimitivesConfig(b = true, i = 42, d = 32.2, c = 'x', s = "string", n = null)
+ val config = Hocon.encodeToConfig(obj)
+
+ config.assertContains("b = true, i = 42, d = 32.2, c = x, s = string, n = null")
+ }
+
+ @Serializable
+ data class ConfigWithEnum(val e: RegularEnum)
+
+ @Serializable
+ enum class RegularEnum { VALUE }
+
+ @Test
+ fun testEncodeConfigWithEnum() {
+ val obj = ConfigWithEnum(RegularEnum.VALUE)
+ val config = Hocon.encodeToConfig(obj)
+
+ config.assertContains("e = VALUE")
+ }
+
+ @Serializable
+ class ConfigWithIterables(
+ val array: BooleanArray,
+ val set: Set<Int>,
+ val list: List<String>,
+ val listNullable: List<Set<SimpleConfig?>?>,
+ )
+
+ @Test
+ fun testEncodeConfigWithIterables() {
+ val obj = ConfigWithIterables(
+ array = booleanArrayOf(true, false),
+ set = setOf(3, 1, 4),
+ list = listOf("A", "B"),
+ listNullable = listOf(null, setOf(SimpleConfig(42), null)),
+ )
+ val config = Hocon.encodeToConfig(obj)
+
+ config.assertContains(
+ """
+ array = [true, false]
+ set = [3, 1, 4]
+ list = [A, B]
+ listNullable = [null, [{ value: 42 }, null]]
+ """
+ )
+ }
+
+ @Serializable
+ data class ConfigWithNested(
+ val nested: SimpleConfig,
+ val nestedList: List<SimpleConfig>,
+ )
+
+ @Test
+ fun testNestedConfigEncoding() {
+ val obj = ConfigWithNested(
+ nested = SimpleConfig(1),
+ nestedList = listOf(SimpleConfig(2)),
+ )
+ val config = Hocon.encodeToConfig(obj)
+
+ config.assertContains("nested { value = 1 }, nestedList = [{ value: 2 }]")
+ }
+
+ @Test
+ fun testMapEncoding() {
+ val objMap = mapOf(
+ "one" to SimpleConfig(1),
+ "two" to SimpleConfig(2),
+ "three" to null,
+ null to SimpleConfig(0),
+ )
+ val config = Hocon.encodeToConfig(objMap)
+
+ config.assertContains(
+ """
+ one { value = 1 }
+ two { value = 2 }
+ three: null
+ null { value = 0 }
+ """
+ )
+ }
+
+ @Serializable
+ data class ConfigWithDefaults(
+ val defInt: Int = 0,
+ val defString: String = "",
+ )
+
+ @Test
+ fun testDefaultsNotEncodedByDefault() {
+ val obj = ConfigWithDefaults(defInt = 42)
+ val config = Hocon.encodeToConfig(obj)
+
+ config.assertContains("defInt = 42")
+ }
+
+ @Test
+ fun testDefaultsEncodedIfEnabled() {
+ val hocon = Hocon { encodeDefaults = true }
+ val obj = ConfigWithDefaults(defInt = 42)
+ val config = hocon.encodeToConfig(obj)
+
+ config.assertContains("defInt = 42, defString = \"\"")
+ }
+
+ @Serializable
+ data class PrimitiveKeysMaps(
+ val number: Map<Int, String>,
+ val boolean: Map<Boolean, String>,
+ val nullable: Map<String?, String>,
+ val enum: Map<RegularEnum, String>,
+ )
+
+ @Test
+ fun testPrimitiveMapKeysEncoding() {
+ val obj = PrimitiveKeysMaps(
+ number = mapOf(42 to "these"),
+ boolean = mapOf(true to "keys"),
+ nullable = mapOf(null to "are"),
+ enum = mapOf(RegularEnum.VALUE to "strings"),
+ )
+ val config = Hocon.encodeToConfig(obj)
+
+ config.assertContains(
+ """
+ number { "42" = these }
+ boolean { "true" = keys }
+ nullable { "null" = are }
+ enum { "VALUE" = strings }
+ """
+ )
+ }
+
+ @Test
+ fun testEncodeMapWithUnsupportedKeys() {
+ assertWrongMapKey("LIST", listOf(1, 1, 2, 3, 5))
+ assertWrongMapKey("OBJECT", mapOf(1 to "one", 2 to "two"))
+ }
+
+ private fun assertWrongMapKey(type: String, key: Any?) {
+ val message = "Value of type '$type' can't be used in HOCON as a key in the map. " +
+ "It should have either primitive or enum kind."
+ val obj = mapOf(key to "value")
+ assertFailsWith<SerializationException>(message) { Hocon.encodeToConfig(obj) }
+ }
+}
diff --git a/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconNamingConventionTest.kt b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconNamingConventionTest.kt
new file mode 100644
index 00000000..889abcd0
--- /dev/null
+++ b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconNamingConventionTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.hocon
+
+import kotlinx.serialization.*
+import org.junit.*
+import org.junit.Assert.*
+
+class HoconNamingConventionTest {
+
+ @Serializable
+ data class CaseConfig(val aCharValue: Char, val aStringValue: String)
+
+ @Serializable
+ data class SerialNameConfig(@SerialName("an-id-value") val anIDValue: Int)
+
+ @Serializable
+ data class CaseWithInnerConfig(val caseConfig: CaseConfig, val serialNameConfig: SerialNameConfig)
+
+ private val hocon = Hocon {
+ useConfigNamingConvention = true
+ }
+
+ @Test
+ fun testDeserializeUsingNamingConvention() {
+ val obj = deserializeConfig("a-char-value = t, a-string-value = test", CaseConfig.serializer(), true)
+ assertEquals('t', obj.aCharValue)
+ assertEquals("test", obj.aStringValue)
+ }
+
+ @Test
+ fun testSerializeUsingNamingConvention() {
+ val obj = CaseConfig(aCharValue = 't', aStringValue = "test")
+ val config = hocon.encodeToConfig(obj)
+
+ config.assertContains("a-char-value = t, a-string-value = test")
+ }
+
+ @Test
+ fun testDeserializeUsingSerialNameInsteadOfNamingConvention() {
+ val obj = deserializeConfig("an-id-value = 42", SerialNameConfig.serializer(), true)
+ assertEquals(42, obj.anIDValue)
+ }
+
+ @Test
+ fun testSerializeUsingSerialNameInsteadOfNamingConvention() {
+ val obj = SerialNameConfig(anIDValue = 42)
+ val config = hocon.encodeToConfig(obj)
+
+ config.assertContains("an-id-value = 42")
+ }
+
+ @Test
+ fun testDeserializeInnerValuesUsingNamingConvention() {
+ val configString = "case-config {a-char-value = b, a-string-value = bar}, serial-name-config {an-id-value = 21}"
+ val obj = deserializeConfig(configString, CaseWithInnerConfig.serializer(), true)
+ with(obj.caseConfig) {
+ assertEquals('b', aCharValue)
+ assertEquals("bar", aStringValue)
+ }
+ assertEquals(21, obj.serialNameConfig.anIDValue)
+ }
+
+ @Test
+ fun testSerializeInnerValuesUsingNamingConvention() {
+ val obj = CaseWithInnerConfig(
+ caseConfig = CaseConfig(aCharValue = 't', aStringValue = "test"),
+ serialNameConfig = SerialNameConfig(anIDValue = 42)
+ )
+ val config = hocon.encodeToConfig(obj)
+
+ config.assertContains(
+ """
+ case-config { a-char-value = t, a-string-value = test }
+ serial-name-config { an-id-value = 42 }
+ """
+ )
+ }
+}
diff --git a/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconObjectsTest.kt b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconObjectsTest.kt
new file mode 100644
index 00000000..a52974f7
--- /dev/null
+++ b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconObjectsTest.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.hocon
+
+import com.typesafe.config.*
+import kotlinx.serialization.*
+import org.junit.*
+import org.junit.Assert.*
+
+internal inline fun <reified T> deserializeConfig(
+ configString: String,
+ deserializer: DeserializationStrategy<T>,
+ useNamingConvention: Boolean = false
+): T {
+ val ucnc = useNamingConvention
+ return Hocon { useConfigNamingConvention = ucnc }
+ .decodeFromConfig(deserializer, ConfigFactory.parseString(configString))
+}
+
+class ConfigParserObjectsTest {
+
+ @Serializable
+ data class Simple(val a: Int)
+
+ @Serializable
+ data class ConfigObjectInner(val e: String, val f: Float = 1.1f)
+
+ @Serializable
+ data class ConfigObject(val a: Int, val b: ConfigObjectInner)
+
+ @Serializable
+ data class ConfWithList(val a: Int, val b: List<Int>)
+
+ @Serializable
+ data class ConfWithMap(val x: Map<String, Int>)
+
+ @Serializable
+ data class NestedObj(val x: List<Simple>)
+
+ @Serializable
+ data class ComplexConfig(
+ val i: Int,
+ val s: String,
+ val iList: List<Int>,
+ val inner: List<Simple>,
+ val ll: List<List<String>>,
+ val m: Map<String, ConfigObjectInner>
+ )
+
+ @Serializable
+ data class VeryComplexConfig(
+ val l: List<Map<String, List<Simple?>>?>,
+ val m: Map<String, NestedObj?>?
+ )
+
+ private val configString = """
+ i = 42
+ s = "foo"
+ iList: [1,2,3]
+ inner = [{ a: 100500 }]
+ ll = [[a, b],[x,z]]
+ m : {
+ kek: {e: foo, f: 5.6 }
+ bar: {e: baz }
+ }
+"""
+
+ private val complexConfigString = """
+ l = [ { x = [ { a:42 }, null] }
+ null
+ ]
+ m = {
+ x: null
+ y: { x = [ {a=43} ] }
+ }
+"""
+
+ @Test
+ fun `complex config`() {
+ val obj = deserializeConfig(configString, ComplexConfig.serializer())
+ with(obj) {
+ assertEquals(42, i)
+ assertEquals("foo", s)
+ assertEquals(listOf(1, 2, 3), iList)
+ assertEquals(listOf(Simple(100500)), inner)
+ assertEquals(listOf(listOf("a", "b"), listOf("x", "z")), ll)
+ assertEquals(mapOf("kek" to ConfigObjectInner("foo", f = 5.6f), "bar" to ConfigObjectInner("baz")), m)
+ }
+ }
+
+ @Test
+ fun `very complex config`() {
+ val obj = deserializeConfig(complexConfigString, VeryComplexConfig.serializer())
+ with(obj) {
+ assertEquals(
+ listOf(
+ mapOf(
+ "x" to listOf(Simple(42), null)
+ ),
+ null
+ ), l
+ )
+ assertEquals(
+ mapOf(
+ "x" to null,
+ "y" to NestedObj(listOf(Simple(43)))
+ ), m
+ )
+ }
+ }
+
+ @Test
+ fun `simple config`() {
+ val conf = ConfigFactory.parseString("a: 42")
+ assertEquals(42, conf.getInt("a"))
+ val simple = Hocon.decodeFromConfig(Simple.serializer(), conf)
+ assertEquals(Simple(42), simple)
+ }
+
+ @Test
+ fun `config with object`() {
+ val conf = ConfigFactory.parseString("a: 42, b: {e = foo}")
+ assertEquals(42, conf.getInt("a"))
+ assertEquals("foo", conf.getString("b.e"))
+ val obj = Hocon.decodeFromConfig(ConfigObject.serializer(), conf)
+ assertEquals(42, obj.a)
+ assertEquals("foo", obj.b.e)
+ assertEquals(1.1f, obj.b.f)
+ }
+
+ @Test
+ fun `config with list`() {
+ val obj = deserializeConfig("a: 42, b: [1,2,3,]", ConfWithList.serializer())
+ assertEquals(42, obj.a)
+ assertEquals(listOf(1, 2, 3), obj.b)
+ }
+
+ @Test
+ fun `config with nested object`() {
+ val obj = deserializeConfig("x: [{a: 42}, {a: 43}, {a: 44}]", NestedObj.serializer())
+ assertEquals(listOf(42, 43, 44).map { Simple(it) }, obj.x)
+ }
+
+ @Test
+ fun `config with map`() {
+ val obj = deserializeConfig("x: { a = 42, b = 43, c = 44 }", ConfWithMap.serializer())
+ assertEquals(mapOf("a" to 42, "b" to 43, "c" to 44), obj.x)
+ }
+}
diff --git a/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconPolymorphismTest.kt b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconPolymorphismTest.kt
new file mode 100644
index 00000000..db038e70
--- /dev/null
+++ b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconPolymorphismTest.kt
@@ -0,0 +1,105 @@
+package kotlinx.serialization.hocon
+
+import kotlinx.serialization.*
+import org.junit.*
+
+class HoconPolymorphismTest {
+ @Serializable
+ sealed class Sealed(val intField: Int) {
+ @Serializable
+ @SerialName("object")
+ object ObjectChild : Sealed(0)
+
+ @Serializable
+ @SerialName("data_class")
+ data class DataClassChild(val name: String) : Sealed(1)
+
+ @Serializable
+ @SerialName("type_child")
+ data class TypeChild(val type: String) : Sealed(2)
+
+ @Serializable
+ @SerialName("annotated_type_child")
+ data class AnnotatedTypeChild(@SerialName("my_type") val type: String) : Sealed(3)
+ }
+
+ @Serializable
+ data class CompositeClass(var sealed: Sealed)
+
+
+ private val arrayHocon = Hocon {
+ useArrayPolymorphism = true
+ }
+
+ private val objectHocon = Hocon {
+ useArrayPolymorphism = false
+ }
+
+
+ @Test
+ fun testArrayDataClass() {
+ arrayHocon.assertStringFormAndRestored(
+ expected = "sealed: [ data_class, { name = testDataClass, intField = 1 } ]",
+ original = CompositeClass(Sealed.DataClassChild("testDataClass")),
+ serializer = CompositeClass.serializer(),
+ )
+ }
+
+ @Test
+ fun testArrayObject() {
+ arrayHocon.assertStringFormAndRestored(
+ expected = "sealed: [ object, {} ]",
+ original = CompositeClass(Sealed.ObjectChild),
+ serializer = CompositeClass.serializer(),
+ )
+ }
+
+ @Test
+ fun testObject() {
+ objectHocon.assertStringFormAndRestored(
+ expected = "type = object",
+ original = Sealed.ObjectChild,
+ serializer = Sealed.serializer(),
+ )
+ }
+
+ @Test
+ fun testNestedDataClass() {
+ objectHocon.assertStringFormAndRestored(
+ expected = "sealed { type = data_class, name = testDataClass, intField = 1 }",
+ original = CompositeClass(Sealed.DataClassChild("testDataClass")),
+ serializer = CompositeClass.serializer(),
+ )
+ }
+
+ @Test
+ fun testDataClassDecode() {
+ objectHocon.assertStringFormAndRestored(
+ expected = "type = data_class, name = testDataClass, intField = 1",
+ original = Sealed.DataClassChild("testDataClass"),
+ serializer = Sealed.serializer(),
+ )
+ }
+
+ @Test
+ fun testChangedDiscriminator() {
+ val hocon = Hocon(objectHocon) {
+ classDiscriminator = "key"
+ }
+
+ hocon.assertStringFormAndRestored(
+ expected = "type = override, key = type_child, intField = 2",
+ original = Sealed.TypeChild(type = "override"),
+ serializer = Sealed.serializer(),
+ )
+ }
+
+ @Test
+ fun testChangedTypePropertyName() {
+ objectHocon.assertStringFormAndRestored(
+ expected = "type = annotated_type_child, my_type = override, intField = 3",
+ original = Sealed.AnnotatedTypeChild(type = "override"),
+ serializer = Sealed.serializer(),
+ )
+ }
+}
diff --git a/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconRootObjectsTest.kt b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconRootObjectsTest.kt
new file mode 100644
index 00000000..3711ba8c
--- /dev/null
+++ b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconRootObjectsTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.hocon
+
+import com.typesafe.config.*
+import kotlinx.serialization.*
+import org.junit.Ignore
+import org.junit.Test
+import kotlin.test.*
+
+class HoconRootMapTest {
+ private val configForRootMap = """
+ key1 {
+ a = "text1"
+ b = 11
+ }
+
+ key2 {
+ a = "text2"
+ b = 12
+ }
+
+ key3 {
+ a = "text3"
+ b = 13
+ }
+ """
+ private val configWithEmptyObject = "{}"
+ private val configWithRootList = "[foo, bar]"
+ private val emptyConfig = ""
+
+ @Serializable
+ data class CompositeValue(
+ val a: String,
+ val b: Int
+ )
+
+ @Test
+ fun testConfigWithRootMap() {
+ val config = ConfigFactory.parseString(configForRootMap)
+ val obj = Hocon.decodeFromConfig<Map<String, CompositeValue>>(config)
+
+ assertEquals(CompositeValue("text1", 11), obj["key1"])
+ assertEquals(CompositeValue("text2", 12), obj["key2"])
+ assertEquals(CompositeValue("text3", 13), obj["key3"])
+ }
+
+ @Test
+ fun testEmptyObjectDecode() {
+ val config = ConfigFactory.parseString(configWithEmptyObject)
+ // non-null map decoded from empty object as empty map
+ val map = Hocon.decodeFromConfig<Map<String, String>>(config)
+ assertTrue(map.isEmpty())
+
+ // nullable map decoded from empty object as null - not obvious
+ assertNull(Hocon.decodeFromConfig<Map<String, String>?>(config))
+
+ // root-level list in config not supported but nullable list can be decoded from empty object
+ assertNull(Hocon.decodeFromConfig<List<String>?>(config))
+ }
+
+ @Test
+ fun testUnsupportedRootObjectsEncode() {
+ assertWrongRootValue("LIST", listOf(1, 1, 2, 3, 5))
+ assertWrongRootValue("NUMBER", 42)
+ assertWrongRootValue("BOOLEAN", false)
+ assertWrongRootValue("NULL", null)
+ assertWrongRootValue("STRING", "string")
+ }
+
+ private fun assertWrongRootValue(type: String, rootValue: Any?) {
+ val message = "Value of type '$type' can't be used at the root of HOCON Config. " +
+ "It should be either object or map."
+ assertFailsWith<SerializationException>(message) { Hocon.encodeToConfig(rootValue) }
+ }
+
+ @Ignore
+ @Test
+ fun testErrors() {
+ // because com.typesafe:config lib not support list in root we can't decode non-null list
+ val config = ConfigFactory.parseString(configWithEmptyObject)
+ assertFailsWith<NoSuchElementException> {
+ Hocon.decodeFromConfig<List<String>>(config)
+ }
+
+ // because com.typesafe:config lib not support list in root it fails while parsing
+ assertFailsWith<Exception> {
+ ConfigFactory.parseString(configWithRootList)
+ }
+
+ // com.typesafe:config lib parse empty config as empty object
+ ConfigFactory.parseString(emptyConfig)
+ }
+}
diff --git a/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconTesting.kt b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconTesting.kt
new file mode 100644
index 00000000..4f54b708
--- /dev/null
+++ b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconTesting.kt
@@ -0,0 +1,24 @@
+package kotlinx.serialization.hocon
+
+import com.typesafe.config.*
+import kotlinx.serialization.*
+import org.junit.Assert.assertEquals
+
+internal inline fun <reified T : Any> Hocon.assertStringFormAndRestored(
+ expected: String,
+ original: T,
+ serializer: KSerializer<T>,
+ printResult: Boolean = false,
+) {
+ val expectedConfig = ConfigFactory.parseString(expected)
+ val config = this.encodeToConfig(serializer, original)
+ if (printResult) println("[Serialized form] $config")
+ assertEquals(expectedConfig, config)
+ val restored = this.decodeFromConfig(serializer, config)
+ if (printResult) println("[Restored form] $restored")
+ assertEquals(original, restored)
+}
+
+internal fun Config.assertContains(expected: String) {
+ assertEquals(ConfigFactory.parseString(expected), this)
+}
diff --git a/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconValuesTest.kt b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconValuesTest.kt
new file mode 100644
index 00000000..07bae736
--- /dev/null
+++ b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconValuesTest.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.hocon
+
+import kotlin.test.*
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import org.junit.Assert.*
+import org.junit.Test
+
+class HoconValuesTest {
+
+ @Serializable
+ data class NumbersConfig(val b: Byte, val s: Short, val i: Int, val l: Long, val f: Float, val d: Double)
+
+ enum class Choice { A, B, C }
+
+ @Serializable
+ data class StringConfig(val c: Char, val s: String)
+
+ @Serializable
+ data class OtherConfig(val b: Boolean, val e: Choice, val u: Unit = Unit)
+
+ @Serializable
+ data class WithDefault(val i: Int = 5, val s: String = "foo")
+
+ @Serializable
+ data class WithNullable(val i: Int?, val s: String?)
+
+ @Serializable
+ data class WithNullableList(val i1: List<Int?>, val i2: List<String>?, val i3: List<WithNullable?>?)
+
+ @Serializable
+ data class WithList(val i1: List<Int>)
+
+ @Serializable
+ data class WithMap(val m: Map<Int, Int>)
+
+ @Test
+ fun `deserialize numbers`() {
+ val conf = "b=42, s=1337, i=100500, l = 4294967294, f=0.0, d=-0.123"
+ val nums = deserializeConfig(conf, NumbersConfig.serializer())
+ with(nums) {
+ assertEquals(42.toByte(), b)
+ assertEquals(1337.toShort(), s)
+ assertEquals(100500, i)
+ assertEquals(4294967294L, l)
+ assertEquals(0.0f, f)
+ assertEquals(-0.123, d, 1e-9)
+ }
+ }
+
+ @Test
+ fun `deserialize numbers from strings`() {
+ val conf = """b="42", s="1337", i="100500", l = "4294967294", f="0.0", d="-0.123" """
+ val nums = deserializeConfig(conf, NumbersConfig.serializer())
+ with(nums) {
+ assertEquals(42.toByte(), b)
+ assertEquals(1337.toShort(), s)
+ assertEquals(100500, i)
+ assertEquals(4294967294L, l)
+ assertEquals(0.0f, f)
+ assertEquals(-0.123, d, 1e-9)
+ }
+ }
+
+ @Test
+ fun `deserialize string types`() {
+ val obj = deserializeConfig("c=f, s=foo", StringConfig.serializer())
+ assertEquals('f', obj.c)
+ assertEquals("foo", obj.s)
+ }
+
+ @Test
+ fun `deserialize other types`() {
+ val obj = deserializeConfig("e = A, b=true", OtherConfig.serializer())
+ assertEquals(Choice.A, obj.e)
+ assertEquals(true, obj.b)
+ }
+
+ @Test
+ fun `unparseable data fails with exception`() {
+ val e = assertFailsWith<SerializationException> {
+ deserializeConfig("e = A, b=not-a-boolean", OtherConfig.serializer())
+ }
+ }
+
+ @Test
+ fun `deserialize other types from strings`() {
+ val obj = deserializeConfig("""e = "A", b="true" """, OtherConfig.serializer())
+ assertEquals(Choice.A, obj.e)
+ assertEquals(true, obj.b)
+ }
+
+ @Test
+ fun `deserialize default values`() {
+ val obj = deserializeConfig("", WithDefault.serializer())
+ assertEquals(5, obj.i)
+ assertEquals("foo", obj.s)
+ }
+
+ @Test
+ fun `overwrite default values`() {
+ val obj = deserializeConfig("i = 42, s = bar", WithDefault.serializer())
+ assertEquals(42, obj.i)
+ assertEquals("bar", obj.s)
+ }
+
+ @Test
+ fun `deserialize nullable types`() {
+ val obj = deserializeConfig("i = 10, s = null", WithNullable.serializer())
+ assertEquals(10, obj.i)
+ assertEquals(null, obj.s)
+ }
+
+ @Test
+ fun `deserialize nullable types with nullable serializer`() {
+ val obj = deserializeConfig("i = 10, s = null", WithNullable.serializer().nullable)!!
+ assertEquals(10, obj.i)
+ assertEquals(null, obj.s)
+ }
+
+ @Test
+ fun testDeserializerTopLevelNullableType() {
+ val value = deserializeConfig("", WithNullable.serializer().nullable)
+ assertNull(value)
+ }
+
+ @Test
+ fun `deserialize complex nullable values`() {
+ val configString = "i1 = [1,null,3], i2=null, i3 = [null, {i: 10, s: bar}]"
+ val obj = deserializeConfig(configString, WithNullableList.serializer())
+ with(obj) {
+ assertEquals(listOf(1, null, 3), i1)
+ assertEquals(null, i2)
+ assertEquals(listOf(null, WithNullable(10, "bar")), i3)
+ }
+ }
+
+ @Test
+ fun `deserialize list of integer string values`() {
+ val configString = """i1 = [ "1","3" ]"""
+ val obj = deserializeConfig(configString, WithList.serializer())
+ assertEquals(listOf(1, 3), obj.i1)
+ }
+
+ @Test
+ fun `deserialize map with integers`() {
+ val configString = """m = { 2: 1, 4: 3 }"""
+ val obj = deserializeConfig(configString, WithMap.serializer())
+ assertEquals(mapOf(2 to 1, 4 to 3), obj.m)
+ }
+
+ @Test
+ fun `deserialize map with integers as strings`() {
+ val configString = """m = { "2": "1", "4":"3" }"""
+ val obj = deserializeConfig(configString, WithMap.serializer())
+ assertEquals(mapOf(2 to 1, 4 to 3), obj.m)
+ }
+}
diff --git a/formats/json/api/kotlinx-serialization-json.api b/formats/json/api/kotlinx-serialization-json.api
new file mode 100644
index 00000000..1fe1440e
--- /dev/null
+++ b/formats/json/api/kotlinx-serialization-json.api
@@ -0,0 +1,357 @@
+public final class kotlinx/serialization/json/DecodeSequenceMode : java/lang/Enum {
+ public static final field ARRAY_WRAPPED Lkotlinx/serialization/json/DecodeSequenceMode;
+ public static final field AUTO_DETECT Lkotlinx/serialization/json/DecodeSequenceMode;
+ public static final field WHITESPACE_SEPARATED Lkotlinx/serialization/json/DecodeSequenceMode;
+ public static fun valueOf (Ljava/lang/String;)Lkotlinx/serialization/json/DecodeSequenceMode;
+ public static fun values ()[Lkotlinx/serialization/json/DecodeSequenceMode;
+}
+
+public abstract class kotlinx/serialization/json/Json : kotlinx/serialization/StringFormat {
+ public static final field Default Lkotlinx/serialization/json/Json$Default;
+ public synthetic fun <init> (Lkotlinx/serialization/json/JsonConfiguration;Lkotlinx/serialization/modules/SerializersModule;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun decodeFromJsonElement (Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/serialization/json/JsonElement;)Ljava/lang/Object;
+ public final fun decodeFromString (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/String;)Ljava/lang/Object;
+ public final fun encodeToJsonElement (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Lkotlinx/serialization/json/JsonElement;
+ public final fun encodeToString (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/lang/String;
+ public final fun getConfiguration ()Lkotlinx/serialization/json/JsonConfiguration;
+ public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+ public final fun parseToJsonElement (Ljava/lang/String;)Lkotlinx/serialization/json/JsonElement;
+}
+
+public final class kotlinx/serialization/json/Json$Default : kotlinx/serialization/json/Json {
+}
+
+public final class kotlinx/serialization/json/JsonArray : kotlinx/serialization/json/JsonElement, java/util/List, kotlin/jvm/internal/markers/KMappedMarker {
+ public static final field Companion Lkotlinx/serialization/json/JsonArray$Companion;
+ public fun <init> (Ljava/util/List;)V
+ public synthetic fun add (ILjava/lang/Object;)V
+ public fun add (ILkotlinx/serialization/json/JsonElement;)V
+ public synthetic fun add (Ljava/lang/Object;)Z
+ public fun add (Lkotlinx/serialization/json/JsonElement;)Z
+ public fun addAll (ILjava/util/Collection;)Z
+ public fun addAll (Ljava/util/Collection;)Z
+ public fun clear ()V
+ public final fun contains (Ljava/lang/Object;)Z
+ public fun contains (Lkotlinx/serialization/json/JsonElement;)Z
+ public fun containsAll (Ljava/util/Collection;)Z
+ public fun equals (Ljava/lang/Object;)Z
+ public synthetic fun get (I)Ljava/lang/Object;
+ public fun get (I)Lkotlinx/serialization/json/JsonElement;
+ public fun getSize ()I
+ public fun hashCode ()I
+ public final fun indexOf (Ljava/lang/Object;)I
+ public fun indexOf (Lkotlinx/serialization/json/JsonElement;)I
+ public fun isEmpty ()Z
+ public fun iterator ()Ljava/util/Iterator;
+ public final fun lastIndexOf (Ljava/lang/Object;)I
+ public fun lastIndexOf (Lkotlinx/serialization/json/JsonElement;)I
+ public fun listIterator ()Ljava/util/ListIterator;
+ public fun listIterator (I)Ljava/util/ListIterator;
+ public synthetic fun remove (I)Ljava/lang/Object;
+ public fun remove (I)Lkotlinx/serialization/json/JsonElement;
+ public fun remove (Ljava/lang/Object;)Z
+ public fun removeAll (Ljava/util/Collection;)Z
+ public fun replaceAll (Ljava/util/function/UnaryOperator;)V
+ public fun retainAll (Ljava/util/Collection;)Z
+ public synthetic fun set (ILjava/lang/Object;)Ljava/lang/Object;
+ public fun set (ILkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/json/JsonElement;
+ public final fun size ()I
+ public fun sort (Ljava/util/Comparator;)V
+ public fun subList (II)Ljava/util/List;
+ public fun toArray ()[Ljava/lang/Object;
+ public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class kotlinx/serialization/json/JsonArray$Companion {
+ public final fun serializer ()Lkotlinx/serialization/KSerializer;
+}
+
+public final class kotlinx/serialization/json/JsonArrayBuilder {
+ public fun <init> ()V
+ public final fun add (Lkotlinx/serialization/json/JsonElement;)Z
+ public final fun build ()Lkotlinx/serialization/json/JsonArray;
+}
+
+public final class kotlinx/serialization/json/JsonArraySerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/json/JsonArraySerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/serialization/json/JsonArray;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/serialization/json/JsonArray;)V
+}
+
+public final class kotlinx/serialization/json/JsonBuilder {
+ public final fun getAllowSpecialFloatingPointValues ()Z
+ public final fun getAllowStructuredMapKeys ()Z
+ public final fun getClassDiscriminator ()Ljava/lang/String;
+ public final fun getCoerceInputValues ()Z
+ public final fun getEncodeDefaults ()Z
+ public final fun getExplicitNulls ()Z
+ public final fun getIgnoreUnknownKeys ()Z
+ public final fun getPrettyPrint ()Z
+ public final fun getPrettyPrintIndent ()Ljava/lang/String;
+ public final fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+ public final fun getUseAlternativeNames ()Z
+ public final fun getUseArrayPolymorphism ()Z
+ public final fun isLenient ()Z
+ public final fun setAllowSpecialFloatingPointValues (Z)V
+ public final fun setAllowStructuredMapKeys (Z)V
+ public final fun setClassDiscriminator (Ljava/lang/String;)V
+ public final fun setCoerceInputValues (Z)V
+ public final fun setEncodeDefaults (Z)V
+ public final fun setExplicitNulls (Z)V
+ public final fun setIgnoreUnknownKeys (Z)V
+ public final fun setLenient (Z)V
+ public final fun setPrettyPrint (Z)V
+ public final fun setPrettyPrintIndent (Ljava/lang/String;)V
+ public final fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V
+ public final fun setUseAlternativeNames (Z)V
+ public final fun setUseArrayPolymorphism (Z)V
+}
+
+public abstract interface annotation class kotlinx/serialization/json/JsonClassDiscriminator : java/lang/annotation/Annotation {
+ public abstract fun discriminator ()Ljava/lang/String;
+}
+
+public final class kotlinx/serialization/json/JsonClassDiscriminator$Impl : kotlinx/serialization/json/JsonClassDiscriminator {
+ public fun <init> (Ljava/lang/String;)V
+ public final synthetic fun discriminator ()Ljava/lang/String;
+}
+
+public final class kotlinx/serialization/json/JsonConfiguration {
+ public fun <init> ()V
+ public final fun getAllowSpecialFloatingPointValues ()Z
+ public final fun getAllowStructuredMapKeys ()Z
+ public final fun getClassDiscriminator ()Ljava/lang/String;
+ public final fun getCoerceInputValues ()Z
+ public final fun getEncodeDefaults ()Z
+ public final fun getExplicitNulls ()Z
+ public final fun getIgnoreUnknownKeys ()Z
+ public final fun getPrettyPrint ()Z
+ public final fun getPrettyPrintIndent ()Ljava/lang/String;
+ public final fun getUseAlternativeNames ()Z
+ public final fun getUseArrayPolymorphism ()Z
+ public final fun isLenient ()Z
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract class kotlinx/serialization/json/JsonContentPolymorphicSerializer : kotlinx/serialization/KSerializer {
+ public fun <init> (Lkotlin/reflect/KClass;)V
+ public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ protected abstract fun selectDeserializer (Lkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/DeserializationStrategy;
+ public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public abstract interface class kotlinx/serialization/json/JsonDecoder : kotlinx/serialization/encoding/CompositeDecoder, kotlinx/serialization/encoding/Decoder {
+ public abstract fun decodeJsonElement ()Lkotlinx/serialization/json/JsonElement;
+ public abstract fun getJson ()Lkotlinx/serialization/json/Json;
+}
+
+public final class kotlinx/serialization/json/JsonDecoder$DefaultImpls {
+ public static fun decodeCollectionSize (Lkotlinx/serialization/json/JsonDecoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public static fun decodeNullableSerializableValue (Lkotlinx/serialization/json/JsonDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ public static fun decodeSequentially (Lkotlinx/serialization/json/JsonDecoder;)Z
+ public static fun decodeSerializableValue (Lkotlinx/serialization/json/JsonDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+}
+
+public abstract class kotlinx/serialization/json/JsonElement {
+ public static final field Companion Lkotlinx/serialization/json/JsonElement$Companion;
+}
+
+public final class kotlinx/serialization/json/JsonElement$Companion {
+ public final fun serializer ()Lkotlinx/serialization/KSerializer;
+}
+
+public final class kotlinx/serialization/json/JsonElementBuildersKt {
+ public static final fun add (Lkotlinx/serialization/json/JsonArrayBuilder;Ljava/lang/Boolean;)Z
+ public static final fun add (Lkotlinx/serialization/json/JsonArrayBuilder;Ljava/lang/Number;)Z
+ public static final fun add (Lkotlinx/serialization/json/JsonArrayBuilder;Ljava/lang/String;)Z
+ public static final fun addJsonArray (Lkotlinx/serialization/json/JsonArrayBuilder;Lkotlin/jvm/functions/Function1;)Z
+ public static final fun addJsonObject (Lkotlinx/serialization/json/JsonArrayBuilder;Lkotlin/jvm/functions/Function1;)Z
+ public static final fun buildJsonArray (Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/json/JsonArray;
+ public static final fun buildJsonObject (Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/json/JsonObject;
+ public static final fun put (Lkotlinx/serialization/json/JsonObjectBuilder;Ljava/lang/String;Ljava/lang/Boolean;)Lkotlinx/serialization/json/JsonElement;
+ public static final fun put (Lkotlinx/serialization/json/JsonObjectBuilder;Ljava/lang/String;Ljava/lang/Number;)Lkotlinx/serialization/json/JsonElement;
+ public static final fun put (Lkotlinx/serialization/json/JsonObjectBuilder;Ljava/lang/String;Ljava/lang/String;)Lkotlinx/serialization/json/JsonElement;
+ public static final fun putJsonArray (Lkotlinx/serialization/json/JsonObjectBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/json/JsonElement;
+ public static final fun putJsonObject (Lkotlinx/serialization/json/JsonObjectBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/json/JsonElement;
+}
+
+public final class kotlinx/serialization/json/JsonElementKt {
+ public static final fun JsonPrimitive (Ljava/lang/Boolean;)Lkotlinx/serialization/json/JsonPrimitive;
+ public static final fun JsonPrimitive (Ljava/lang/Number;)Lkotlinx/serialization/json/JsonPrimitive;
+ public static final fun JsonPrimitive (Ljava/lang/String;)Lkotlinx/serialization/json/JsonPrimitive;
+ public static final fun getBoolean (Lkotlinx/serialization/json/JsonPrimitive;)Z
+ public static final fun getBooleanOrNull (Lkotlinx/serialization/json/JsonPrimitive;)Ljava/lang/Boolean;
+ public static final fun getContentOrNull (Lkotlinx/serialization/json/JsonPrimitive;)Ljava/lang/String;
+ public static final fun getDouble (Lkotlinx/serialization/json/JsonPrimitive;)D
+ public static final fun getDoubleOrNull (Lkotlinx/serialization/json/JsonPrimitive;)Ljava/lang/Double;
+ public static final fun getFloat (Lkotlinx/serialization/json/JsonPrimitive;)F
+ public static final fun getFloatOrNull (Lkotlinx/serialization/json/JsonPrimitive;)Ljava/lang/Float;
+ public static final fun getInt (Lkotlinx/serialization/json/JsonPrimitive;)I
+ public static final fun getIntOrNull (Lkotlinx/serialization/json/JsonPrimitive;)Ljava/lang/Integer;
+ public static final fun getJsonArray (Lkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/json/JsonArray;
+ public static final fun getJsonNull (Lkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/json/JsonNull;
+ public static final fun getJsonObject (Lkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/json/JsonObject;
+ public static final fun getJsonPrimitive (Lkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/json/JsonPrimitive;
+ public static final fun getLong (Lkotlinx/serialization/json/JsonPrimitive;)J
+ public static final fun getLongOrNull (Lkotlinx/serialization/json/JsonPrimitive;)Ljava/lang/Long;
+ public static final fun unexpectedJson (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Void;
+}
+
+public final class kotlinx/serialization/json/JsonElementSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/json/JsonElementSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/serialization/json/JsonElement;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/serialization/json/JsonElement;)V
+}
+
+public abstract interface class kotlinx/serialization/json/JsonEncoder : kotlinx/serialization/encoding/CompositeEncoder, kotlinx/serialization/encoding/Encoder {
+ public abstract fun encodeJsonElement (Lkotlinx/serialization/json/JsonElement;)V
+ public abstract fun getJson ()Lkotlinx/serialization/json/Json;
+}
+
+public final class kotlinx/serialization/json/JsonEncoder$DefaultImpls {
+ public static fun beginCollection (Lkotlinx/serialization/json/JsonEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder;
+ public static fun encodeNotNullMark (Lkotlinx/serialization/json/JsonEncoder;)V
+ public static fun encodeNullableSerializableValue (Lkotlinx/serialization/json/JsonEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public static fun encodeSerializableValue (Lkotlinx/serialization/json/JsonEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public static fun shouldEncodeElementDefault (Lkotlinx/serialization/json/JsonEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
+}
+
+public final class kotlinx/serialization/json/JsonKt {
+ public static final fun Json (Lkotlinx/serialization/json/Json;Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/json/Json;
+ public static synthetic fun Json$default (Lkotlinx/serialization/json/Json;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/serialization/json/Json;
+}
+
+public abstract interface annotation class kotlinx/serialization/json/JsonNames : java/lang/annotation/Annotation {
+ public abstract fun names ()[Ljava/lang/String;
+}
+
+public final class kotlinx/serialization/json/JsonNames$Impl : kotlinx/serialization/json/JsonNames {
+ public fun <init> ([Ljava/lang/String;)V
+ public final synthetic fun names ()[Ljava/lang/String;
+}
+
+public final class kotlinx/serialization/json/JsonNull : kotlinx/serialization/json/JsonPrimitive {
+ public static final field INSTANCE Lkotlinx/serialization/json/JsonNull;
+ public fun getContent ()Ljava/lang/String;
+ public fun isString ()Z
+ public final fun serializer ()Lkotlinx/serialization/KSerializer;
+}
+
+public final class kotlinx/serialization/json/JsonNullSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/json/JsonNullSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/serialization/json/JsonNull;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/serialization/json/JsonNull;)V
+}
+
+public final class kotlinx/serialization/json/JsonObject : kotlinx/serialization/json/JsonElement, java/util/Map, kotlin/jvm/internal/markers/KMappedMarker {
+ public static final field Companion Lkotlinx/serialization/json/JsonObject$Companion;
+ public fun <init> (Ljava/util/Map;)V
+ public fun clear ()V
+ public synthetic fun compute (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;
+ public fun compute (Ljava/lang/String;Ljava/util/function/BiFunction;)Lkotlinx/serialization/json/JsonElement;
+ public synthetic fun computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;
+ public fun computeIfAbsent (Ljava/lang/String;Ljava/util/function/Function;)Lkotlinx/serialization/json/JsonElement;
+ public synthetic fun computeIfPresent (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;
+ public fun computeIfPresent (Ljava/lang/String;Ljava/util/function/BiFunction;)Lkotlinx/serialization/json/JsonElement;
+ public final fun containsKey (Ljava/lang/Object;)Z
+ public fun containsKey (Ljava/lang/String;)Z
+ public final fun containsValue (Ljava/lang/Object;)Z
+ public fun containsValue (Lkotlinx/serialization/json/JsonElement;)Z
+ public final fun entrySet ()Ljava/util/Set;
+ public fun equals (Ljava/lang/Object;)Z
+ public final synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object;
+ public final fun get (Ljava/lang/Object;)Lkotlinx/serialization/json/JsonElement;
+ public fun get (Ljava/lang/String;)Lkotlinx/serialization/json/JsonElement;
+ public fun getEntries ()Ljava/util/Set;
+ public fun getKeys ()Ljava/util/Set;
+ public fun getSize ()I
+ public fun getValues ()Ljava/util/Collection;
+ public fun hashCode ()I
+ public fun isEmpty ()Z
+ public final fun keySet ()Ljava/util/Set;
+ public synthetic fun merge (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;
+ public fun merge (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Ljava/util/function/BiFunction;)Lkotlinx/serialization/json/JsonElement;
+ public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun put (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/json/JsonElement;
+ public fun putAll (Ljava/util/Map;)V
+ public synthetic fun putIfAbsent (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun putIfAbsent (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/json/JsonElement;
+ public synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object;
+ public fun remove (Ljava/lang/Object;)Lkotlinx/serialization/json/JsonElement;
+ public fun remove (Ljava/lang/Object;Ljava/lang/Object;)Z
+ public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z
+ public fun replace (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/json/JsonElement;
+ public fun replace (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;)Z
+ public fun replaceAll (Ljava/util/function/BiFunction;)V
+ public final fun size ()I
+ public fun toString ()Ljava/lang/String;
+ public final fun values ()Ljava/util/Collection;
+}
+
+public final class kotlinx/serialization/json/JsonObject$Companion {
+ public final fun serializer ()Lkotlinx/serialization/KSerializer;
+}
+
+public final class kotlinx/serialization/json/JsonObjectBuilder {
+ public fun <init> ()V
+ public final fun build ()Lkotlinx/serialization/json/JsonObject;
+ public final fun put (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/json/JsonElement;
+}
+
+public final class kotlinx/serialization/json/JsonObjectSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/json/JsonObjectSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/serialization/json/JsonObject;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/serialization/json/JsonObject;)V
+}
+
+public abstract class kotlinx/serialization/json/JsonPrimitive : kotlinx/serialization/json/JsonElement {
+ public static final field Companion Lkotlinx/serialization/json/JsonPrimitive$Companion;
+ public abstract fun getContent ()Ljava/lang/String;
+ public abstract fun isString ()Z
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class kotlinx/serialization/json/JsonPrimitive$Companion {
+ public final fun serializer ()Lkotlinx/serialization/KSerializer;
+}
+
+public final class kotlinx/serialization/json/JsonPrimitiveSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/json/JsonPrimitiveSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/serialization/json/JsonPrimitive;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/serialization/json/JsonPrimitive;)V
+}
+
+public abstract class kotlinx/serialization/json/JsonTransformingSerializer : kotlinx/serialization/KSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;)V
+ public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ protected fun transformDeserialize (Lkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/json/JsonElement;
+ protected fun transformSerialize (Lkotlinx/serialization/json/JsonElement;)Lkotlinx/serialization/json/JsonElement;
+}
+
+public final class kotlinx/serialization/json/JvmStreamsKt {
+ public static final fun decodeFromStream (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/DeserializationStrategy;Ljava/io/InputStream;)Ljava/lang/Object;
+ public static final fun decodeToSequence (Lkotlinx/serialization/json/Json;Ljava/io/InputStream;Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/serialization/json/DecodeSequenceMode;)Lkotlin/sequences/Sequence;
+ public static synthetic fun decodeToSequence$default (Lkotlinx/serialization/json/Json;Ljava/io/InputStream;Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/serialization/json/DecodeSequenceMode;ILjava/lang/Object;)Lkotlin/sequences/Sequence;
+ public static final fun encodeToStream (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Ljava/io/OutputStream;)V
+}
+
diff --git a/formats/json/build.gradle b/formats/json/build.gradle
new file mode 100644
index 00000000..22944459
--- /dev/null
+++ b/formats/json/build.gradle
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+apply plugin: 'kotlin-multiplatform'
+apply plugin: 'kotlinx-serialization'
+apply from: rootProject.file("gradle/native-targets.gradle")
+apply from: rootProject.file("gradle/configure-source-sets.gradle")
+
+kotlin {
+
+ sourceSets {
+ commonMain {
+ dependencies {
+ api project(":kotlinx-serialization-core")
+ }
+ }
+
+ jvmTest {
+ dependencies {
+ implementation 'com.google.code.gson:gson:2.8.5'
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
+ }
+ }
+ }
+}
+
+compileTestKotlinJsLegacy {
+ exclude '**/PropertyInitializerTest.kt'
+}
+
+Java9Modularity.configureJava9ModuleInfo(project)
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt b/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt
new file mode 100644
index 00000000..3cdcf109
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.modules.*
+import kotlin.native.concurrent.*
+
+/**
+ * The main entry point to work with JSON serialization.
+ * It is typically used by constructing an application-specific instance, with configured JSON-specific behaviour
+ * and, if necessary, registered in [SerializersModule] custom serializers.
+ * `Json` instance can be configured in its `Json {}` factory function using [JsonBuilder].
+ * For demonstration purposes or trivial usages, Json [companion][Json.Default] can be used instead.
+ *
+ * Then constructed instance can be used either as regular [SerialFormat] or [StringFormat]
+ * or for converting objects to [JsonElement] back and forth.
+ *
+ * This is the only serial format which has the first-class [JsonElement] support.
+ * Any serializable class can be serialized to or from [JsonElement] with [Json.decodeFromJsonElement] and [Json.encodeToJsonElement] respectively or
+ * serialize properties of [JsonElement] type.
+ *
+ * Example of usage:
+ * ```
+ * @Serializable
+ * class DataHolder(val id: Int, val data: String, val extensions: JsonElement)
+ *
+ * val json = Json
+ * val instance = DataHolder(42, "some data", buildJsonObject { put("additional key", "value") }
+ *
+ * // Plain StringFormat usage
+ * val stringOutput: String = json.encodeToString(instance)
+ *
+ * // JsonElement serialization specific for JSON only
+ * val jsonTree: JsonElement = json.encodeToJsonElement(instance)
+ *
+ * // Deserialize from string
+ * val deserialized: DataHolder = json.decodeFromString<DataHolder>(stringOutput)
+ *
+ * // Deserialize from json tree, JSON-specific
+ * val deserializedFromTree: DataHolder = json.decodeFromJsonElement<DataHolder>(jsonTree)
+ *
+ * // Deserialize from string to JSON tree, JSON-specific
+ * val deserializedToTree: JsonElement = json.parseToJsonElement(stringOutput)
+ * ```
+ *
+ * Json instance also exposes its [configuration] that can be used in custom serializers
+ * that rely on [JsonDecoder] and [JsonEncoder] for customizable behaviour.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public sealed class Json(
+ public val configuration: JsonConfiguration,
+ override val serializersModule: SerializersModule
+) : StringFormat {
+
+ @Deprecated(
+ "Should not be accessed directly, use Json.schemaCache accessor instead",
+ ReplaceWith("schemaCache"),
+ DeprecationLevel.ERROR
+ )
+ internal val _schemaCache: DescriptorSchemaCache = DescriptorSchemaCache()
+
+ /**
+ * The default instance of [Json] with default configuration.
+ */
+ @ThreadLocal // to support caching
+ public companion object Default : Json(JsonConfiguration(), EmptySerializersModule)
+
+ /**
+ * Serializes the [value] into an equivalent JSON using the given [serializer].
+ *
+ * @throws [SerializationException] if the given value cannot be serialized to JSON.
+ */
+ public final override fun <T> encodeToString(serializer: SerializationStrategy<T>, value: T): String {
+ val result = JsonStringBuilder()
+ try {
+ val encoder = StreamingJsonEncoder(
+ result, this,
+ WriteMode.OBJ,
+ arrayOfNulls(WriteMode.values().size)
+ )
+ encoder.encodeSerializableValue(serializer, value)
+ return result.toString()
+ } finally {
+ result.release()
+ }
+ }
+
+ /**
+ * Deserializes the given JSON [string] into a value of type [T] using the given [deserializer].
+ *
+ * @throws [SerializationException] if the given JSON string cannot be deserialized to the value of type [T].
+ */
+ public final override fun <T> decodeFromString(deserializer: DeserializationStrategy<T>, string: String): T {
+ val lexer = StringJsonLexer(string)
+ val input = StreamingJsonDecoder(this, WriteMode.OBJ, lexer, deserializer.descriptor)
+ val result = input.decodeSerializableValue(deserializer)
+ lexer.expectEof()
+ return result
+ }
+ /**
+ * Serializes the given [value] into an equivalent [JsonElement] using the given [serializer]
+ *
+ * @throws [SerializationException] if the given value cannot be serialized.
+ */
+ public fun <T> encodeToJsonElement(serializer: SerializationStrategy<T>, value: T): JsonElement {
+ return writeJson(value, serializer)
+ }
+
+ /**
+ * Deserializes the given [element] into a value of type [T] using the given [deserializer].
+ *
+ * @throws [SerializationException] if the given JSON string cannot be deserialized to the value of type [T].
+ */
+ public fun <T> decodeFromJsonElement(deserializer: DeserializationStrategy<T>, element: JsonElement): T {
+ return readJson(element, deserializer)
+ }
+
+ /**
+ * Deserializes the given JSON [string] into a corresponding [JsonElement] representation.
+ *
+ * @throws [SerializationException] if the given JSON string is malformed and cannot be deserialized
+ */
+ public fun parseToJsonElement(string: String): JsonElement {
+ return decodeFromString(JsonElementSerializer, string)
+ }
+}
+
+/**
+ * Creates an instance of [Json] configured from the optionally given [Json instance][from] and adjusted with [builderAction].
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public fun Json(from: Json = Json.Default, builderAction: JsonBuilder.() -> Unit): Json {
+ val builder = JsonBuilder(from)
+ builder.builderAction()
+ val conf = builder.build()
+ return JsonImpl(conf, builder.serializersModule)
+}
+
+/**
+ * Serializes the given [value] into an equivalent [JsonElement] using a serializer retrieved
+ * from reified type parameter.
+ *
+ * @throws [SerializationException] if the given value cannot be serialized to JSON.
+ */
+public inline fun <reified T> Json.encodeToJsonElement(value: T): JsonElement {
+ return encodeToJsonElement(serializersModule.serializer(), value)
+}
+
+/**
+ * Deserializes the given [json] element into a value of type [T] using a deserializer retrieved
+ * from reified type parameter.
+ *
+ * @throws [SerializationException] if the given JSON string is malformed or cannot be deserialized to the value of type [T].
+ */
+public inline fun <reified T> Json.decodeFromJsonElement(json: JsonElement): T =
+ decodeFromJsonElement(serializersModule.serializer(), json)
+
+/**
+ * Builder of the [Json] instance provided by `Json { ... }` factory function.
+ */
+@Suppress("unused", "DeprecatedCallableAddReplaceWith")
+@OptIn(ExperimentalSerializationApi::class)
+public class JsonBuilder internal constructor(json: Json) {
+ /**
+ * Specifies whether default values of Kotlin properties should be encoded.
+ * `false` by default.
+ */
+ public var encodeDefaults: Boolean = json.configuration.encodeDefaults
+
+ /**
+ * Specifies whether `null` values should be encoded for nullable properties and must be present in JSON object
+ * during decoding.
+ *
+ * When this flag is disabled properties with `null` values without default are not encoded;
+ * during decoding, the absence of a field value is treated as `null` for nullable properties without a default value.
+ *
+ * `true` by default.
+ */
+ @ExperimentalSerializationApi
+ public var explicitNulls: Boolean = json.configuration.explicitNulls
+
+ /**
+ * Specifies whether encounters of unknown properties in the input JSON
+ * should be ignored instead of throwing [SerializationException].
+ * `false` by default.
+ */
+ public var ignoreUnknownKeys: Boolean = json.configuration.ignoreUnknownKeys
+
+ /**
+ * Removes JSON specification restriction (RFC-4627) and makes parser
+ * more liberal to the malformed input. In lenient mode quoted boolean literals,
+ * and unquoted string literals are allowed.
+ *
+ * Its relaxations can be expanded in the future, so that lenient parser becomes even more
+ * permissive to invalid value in the input, replacing them with defaults.
+ *
+ * `false` by default.
+ */
+ public var isLenient: Boolean = json.configuration.isLenient
+
+ /**
+ * Enables structured objects to be serialized as map keys by
+ * changing serialized form of the map from JSON object (key-value pairs) to flat array like `[k1, v1, k2, v2]`.
+ * `false` by default.
+ */
+ public var allowStructuredMapKeys: Boolean = json.configuration.allowStructuredMapKeys
+
+ /**
+ * Specifies whether resulting JSON should be pretty-printed.
+ * `false` by default.
+ */
+ public var prettyPrint: Boolean = json.configuration.prettyPrint
+
+ /**
+ * Specifies indent string to use with [prettyPrint] mode
+ * 4 spaces by default.
+ * Experimentality note: this API is experimental because
+ * it is not clear whether this option has compelling use-cases.
+ */
+ @ExperimentalSerializationApi
+ public var prettyPrintIndent: String = json.configuration.prettyPrintIndent
+
+ /**
+ * Enables coercing incorrect JSON values to the default property value in the following cases:
+ * 1. JSON value is `null` but property type is non-nullable.
+ * 2. Property type is an enum type, but JSON value contains unknown enum member.
+ *
+ * `false` by default.
+ */
+ public var coerceInputValues: Boolean = json.configuration.coerceInputValues
+
+ /**
+ * Switches polymorphic serialization to the default array format.
+ * This is an option for legacy JSON format and should not be generally used.
+ * `false` by default.
+ */
+ public var useArrayPolymorphism: Boolean = json.configuration.useArrayPolymorphism
+
+ /**
+ * Name of the class descriptor property for polymorphic serialization.
+ * "type" by default.
+ */
+ public var classDiscriminator: String = json.configuration.classDiscriminator
+
+ /**
+ * Removes JSON specification restriction on
+ * special floating-point values such as `NaN` and `Infinity` and enables their serialization and deserialization.
+ * When enabling it, please ensure that the receiving party will be able to encode and decode these special values.
+ * `false` by default.
+ */
+ public var allowSpecialFloatingPointValues: Boolean = json.configuration.allowSpecialFloatingPointValues
+
+ /**
+ * Specifies whether Json instance makes use of [JsonNames] annotation.
+ *
+ * Disabling this flag when one does not use [JsonNames] at all may sometimes result in better performance,
+ * particularly when a large count of fields is skipped with [ignoreUnknownKeys].
+ * `true` by default.
+ */
+ public var useAlternativeNames: Boolean = json.configuration.useAlternativeNames
+
+ /**
+ * Module with contextual and polymorphic serializers to be used in the resulting [Json] instance.
+ */
+ public var serializersModule: SerializersModule = json.serializersModule
+
+ @OptIn(ExperimentalSerializationApi::class)
+ internal fun build(): JsonConfiguration {
+ if (useArrayPolymorphism) require(classDiscriminator == defaultDiscriminator) {
+ "Class discriminator should not be specified when array polymorphism is specified"
+ }
+
+ if (!prettyPrint) {
+ require(prettyPrintIndent == defaultIndent) {
+ "Indent should not be specified when default printing mode is used"
+ }
+ } else if (prettyPrintIndent != defaultIndent) {
+ // Values allowed by JSON specification as whitespaces
+ val allWhitespaces = prettyPrintIndent.all { it == ' ' || it == '\t' || it == '\r' || it == '\n' }
+ require(allWhitespaces) {
+ "Only whitespace, tab, newline and carriage return are allowed as pretty print symbols. Had $prettyPrintIndent"
+ }
+ }
+
+ return JsonConfiguration(
+ encodeDefaults, ignoreUnknownKeys, isLenient,
+ allowStructuredMapKeys, prettyPrint, explicitNulls, prettyPrintIndent,
+ coerceInputValues, useArrayPolymorphism,
+ classDiscriminator, allowSpecialFloatingPointValues, useAlternativeNames
+ )
+ }
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private class JsonImpl(configuration: JsonConfiguration, module: SerializersModule) : Json(configuration, module) {
+
+ init {
+ validateConfiguration()
+ }
+
+ private fun validateConfiguration() {
+ if (serializersModule == EmptySerializersModule) return // Fast-path for in-place JSON allocations
+ val collector = PolymorphismValidator(configuration.useArrayPolymorphism, configuration.classDiscriminator)
+ serializersModule.dumpTo(collector)
+ }
+}
+
+/**
+ * This accessor should be used to workaround for freezing problems in Native, see Native source set
+ */
+internal expect val Json.schemaCache: DescriptorSchemaCache
+
+private const val defaultIndent = " "
+private const val defaultDiscriminator = "type"
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonAnnotations.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonAnnotations.kt
new file mode 100644
index 00000000..aae69889
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonAnnotations.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.internal.*
+import kotlin.native.concurrent.*
+
+/**
+ * Indicates that the field can be represented in JSON
+ * with multiple possible alternative names.
+ * [Json] format recognizes this annotation and is able to decode
+ * the data using any of the alternative names.
+ *
+ * Unlike [SerialName] annotation, does not affect JSON encoding in any way.
+ *
+ * Example of usage:
+ * ```
+ * @Serializable
+ * data class Project(@JsonNames("title") val name: String)
+ *
+ * val project = Json.decodeFromString<Project>("""{"name":"kotlinx.serialization"}""")
+ * println(project)
+ * val oldProject = Json.decodeFromString<Project>("""{"title":"kotlinx.coroutines"}""")
+ * println(oldProject)
+ * ```
+ *
+ * This annotation has lesser priority than [SerialName].
+ *
+ * @see JsonBuilder.useAlternativeNames
+ */
+@SerialInfo
+@Target(AnnotationTarget.PROPERTY)
+@ExperimentalSerializationApi
+public annotation class JsonNames(vararg val names: String)
+
+/**
+ * Specifies key for class discriminator value used during polymorphic serialization in [Json].
+ * Provided key is used only for an annotated class and its subclasses;
+ * to configure global class discriminator, use [JsonBuilder.classDiscriminator]
+ * property.
+ *
+ * This annotation is [inheritable][InheritableSerialInfo], so it should be sufficient to place it on a base class of hierarchy.
+ * It is not possible to define different class discriminators for different parts of class hierarchy.
+ * Pay attention to the fact that class discriminator, same as polymorphic serializer's base class, is
+ * determined statically.
+ *
+ * Example:
+ * ```
+ * @Serializable
+ * @JsonClassDiscriminator("message_type")
+ * abstract class Base
+ *
+ * @Serializable // Class discriminator is inherited from Base
+ * abstract class ErrorClass: Base()
+ *
+ * @Serializable
+ * class Message(val message: Base, val error: ErrorClass?)
+ *
+ * val message = Json.decodeFromString<Message>("""{"message": {"message_type":"my.app.BaseMessage", "message": "not found"}, "error": {"message_type":"my.app.GenericError", "error_code": 404}}""")
+ * ```
+ *
+ * @see JsonBuilder.classDiscriminator
+ */
+@InheritableSerialInfo
+@Target(AnnotationTarget.CLASS)
+@ExperimentalSerializationApi
+public annotation class JsonClassDiscriminator(val discriminator: String)
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonConfiguration.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonConfiguration.kt
new file mode 100644
index 00000000..612cfc7c
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonConfiguration.kt
@@ -0,0 +1,42 @@
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+
+/**
+ * Configuration of the current [Json] instance available through [Json.configuration]
+ * and configured with [JsonBuilder] constructor.
+ *
+ * Can be used for debug purposes and for custom Json-specific serializers
+ * via [JsonEncoder] and [JsonDecoder].
+ *
+ * Standalone configuration object is meaningless and can nor be used outside of the
+ * [Json], neither new [Json] instance can be created from it.
+ *
+ * Detailed description of each property is available in [JsonBuilder] class.
+ */
+public class JsonConfiguration internal constructor(
+ public val encodeDefaults: Boolean = false,
+ public val ignoreUnknownKeys: Boolean = false,
+ public val isLenient: Boolean = false,
+ public val allowStructuredMapKeys: Boolean = false,
+ public val prettyPrint: Boolean = false,
+ @ExperimentalSerializationApi
+ public val explicitNulls: Boolean = true,
+ @ExperimentalSerializationApi
+ public val prettyPrintIndent: String = " ",
+ public val coerceInputValues: Boolean = false,
+ public val useArrayPolymorphism: Boolean = false,
+ public val classDiscriminator: String = "type",
+ public val allowSpecialFloatingPointValues: Boolean = false,
+ public val useAlternativeNames: Boolean = true
+) {
+
+ /** @suppress Dokka **/
+ @OptIn(ExperimentalSerializationApi::class)
+ override fun toString(): String {
+ return "JsonConfiguration(encodeDefaults=$encodeDefaults, ignoreUnknownKeys=$ignoreUnknownKeys, isLenient=$isLenient, " +
+ "allowStructuredMapKeys=$allowStructuredMapKeys, prettyPrint=$prettyPrint, explicitNulls=$explicitNulls, " +
+ "prettyPrintIndent='$prettyPrintIndent', coerceInputValues=$coerceInputValues, useArrayPolymorphism=$useArrayPolymorphism, " +
+ "classDiscriminator='$classDiscriminator', allowSpecialFloatingPointValues=$allowSpecialFloatingPointValues)"
+ }
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonContentPolymorphicSerializer.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonContentPolymorphicSerializer.kt
new file mode 100644
index 00000000..7255a59f
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonContentPolymorphicSerializer.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import kotlin.reflect.*
+
+/**
+ * Base class for custom serializers that allows selecting polymorphic serializer
+ * without a dedicated class discriminator, on a content basis.
+ *
+ * Usually, polymorphic serialization (represented by [PolymorphicSerializer] and [SealedClassSerializer])
+ * requires a dedicated `"type"` property in the JSON to
+ * determine actual serializer that is used to deserialize Kotlin class.
+ *
+ * However, sometimes (e.g. when interacting with external API) type property is not present in the input
+ * and it is expected to guess the actual type by the shape of JSON, for example by the presence of specific key.
+ * [JsonContentPolymorphicSerializer] provides a skeleton implementation for such strategy. Please note that
+ * since JSON content is represented by [JsonElement] class and could be read only with [JsonDecoder] decoder,
+ * this class works only with [Json] format.
+ *
+ * Deserialization happens in two stages: first, a value from the input JSON is read
+ * to as a [JsonElement]. Second, [selectDeserializer] function is called to determine which serializer should be used.
+ * The returned serializer is used to deserialize [JsonElement] back to Kotlin object.
+ *
+ * It is possible to serialize values this serializer. In that case, class discriminator property won't
+ * be added to JSON stream, i.e., deserializing a class from the string and serializing it back yields the original string.
+ * However, to determine a serializer, a standard polymorphic mechanism represented by [SerializersModule] is used.
+ * For convenience, [serialize] method can lookup default serializer, but it is recommended to follow
+ * standard procedure with [registering][SerializersModuleBuilder.polymorphic].
+ *
+ * Usage example:
+ * ```
+ * interface Payment {
+ * val amount: String
+ * }
+ *
+ * @Serializable
+ * data class SuccessfulPayment(override val amount: String, val date: String) : Payment
+ *
+ * @Serializable
+ * data class RefundedPayment(override val amount: String, val date: String, val reason: String) : Payment
+ *
+ * object PaymentSerializer : JsonContentPolymorphicSerializer<Payment>(Payment::class) {
+ * override fun selectDeserializer(content: JsonElement) = when {
+ * "reason" in content.jsonObject -> RefundedPayment.serializer()
+ * else -> SuccessfulPayment.serializer()
+ * }
+ * }
+ *
+ * // Now both statements will yield different subclasses of Payment:
+ *
+ * Json.parse(PaymentSerializer, """{"amount":"1.0","date":"03.02.2020"}""")
+ * Json.parse(PaymentSerializer, """{"amount":"2.0","date":"03.02.2020","reason":"complaint"}""")
+ * ```
+ *
+ * @param T A root class for all classes that could be possibly encountered during serialization and deserialization.
+ * @param baseClass A class token for [T].
+ */
+@OptIn(ExperimentalSerializationApi::class)
+public abstract class JsonContentPolymorphicSerializer<T : Any>(private val baseClass: KClass<T>) : KSerializer<T> {
+ /**
+ * A descriptor for this set of content-based serializers.
+ * By default, it uses the name composed of [baseClass] simple name,
+ * kind is set to [PolymorphicKind.SEALED] and contains 0 elements.
+ *
+ * However, this descriptor can be overridden to achieve better representation of custom transformed JSON shape
+ * for schema generating/introspection purposes.
+ */
+ override val descriptor: SerialDescriptor =
+ buildSerialDescriptor("JsonContentPolymorphicSerializer<${baseClass.simpleName}>", PolymorphicKind.SEALED)
+
+ final override fun serialize(encoder: Encoder, value: T) {
+ val actualSerializer =
+ encoder.serializersModule.getPolymorphic(baseClass, value)
+ ?: value::class.serializerOrNull()
+ ?: throwSubtypeNotRegistered(value::class, baseClass)
+ @Suppress("UNCHECKED_CAST")
+ (actualSerializer as KSerializer<T>).serialize(encoder, value)
+ }
+
+ final override fun deserialize(decoder: Decoder): T {
+ val input = decoder.asJsonDecoder()
+ val tree = input.decodeJsonElement()
+
+ @Suppress("UNCHECKED_CAST")
+ val actualSerializer = selectDeserializer(tree) as KSerializer<T>
+ return input.json.decodeFromJsonElement(actualSerializer, tree)
+ }
+
+ /**
+ * Determines a particular strategy for deserialization by looking on a parsed JSON [element].
+ */
+ protected abstract fun selectDeserializer(element: JsonElement): DeserializationStrategy<out T>
+
+ private fun throwSubtypeNotRegistered(subClass: KClass<*>, baseClass: KClass<*>): Nothing {
+ val subClassName = subClass.simpleName ?: "$subClass"
+ val scope = "in the scope of '${baseClass.simpleName}'"
+ throw SerializationException(
+ "Class '${subClassName}' is not registered for polymorphic serialization $scope.\n" +
+ "Mark the base class as 'sealed' or register the serializer explicitly.")
+ }
+
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonDecoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonDecoder.kt
new file mode 100644
index 00000000..2ccc46a4
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonDecoder.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+/**
+ * Decoder used by [Json] during deserialization.
+ * This interface can be used to inject desired behaviour into a serialization process of [Json].
+ *
+ * Typical example of the usage:
+ * ```
+ * // Class representing Either<Left|Right>
+ * sealed class Either {
+ * data class Left(val errorMsg: String) : Either()
+ * data class Right(val data: Payload) : Either()
+ * }
+ *
+ * // Serializer injects custom behaviour by inspecting object content and writing
+ * object EitherSerializer : KSerializer<Either> {
+ * override val descriptor: SerialDescriptor = buildSerialDescriptor("package.Either", PolymorphicKind.SEALED) {
+ * // ..
+ * }
+ *
+ * override fun deserialize(decoder: Decoder): Either {
+ * val input = decoder as? JsonDecoder ?: throw SerializationException("This class can be decoded only by Json format")
+ * val tree = input.decodeJsonElement() as? JsonObject ?: throw SerializationException("Expected JsonObject")
+ * if ("error" in tree) return Either.Left(tree["error"]!!.jsonPrimitive.content)
+ * return Either.Right(input.json.decodeFromJsonElement(Payload.serializer(), tree))
+ * }
+ *
+ * override fun serialize(encoder: Encoder, value: Either) {
+ * val output = encoder as? JsonEncoder ?: throw SerializationException("This class can be encoded only by Json format")
+ * val tree = when (value) {
+ * is Either.Left -> JsonObject(mapOf("error" to JsonPrimitive(value.errorMsg)))
+ * is Either.Right -> output.json.encodeToJsonElement(Payload.serializer(), value.data)
+ * }
+ * output.encodeJsonElement(tree)
+ * }
+ * }
+ * ```
+ *
+ * ### Not stable for inheritance
+ *
+ * `JsonDecoder` interface is not stable for inheritance in 3rd party libraries, as new methods
+ * might be added to this interface or contracts of the existing methods can be changed.
+ * Accepting this interface in your API methods, casting [Decoder] to [JsonDecoder] and invoking its
+ * methods is considered stable.
+ */
+public interface JsonDecoder : Decoder, CompositeDecoder {
+ /**
+ * An instance of the current [Json].
+ */
+ public val json: Json
+
+ /**
+ * Decodes the next element in the current input as [JsonElement].
+ * The type of the decoded element depends on the current state of the input and, when received
+ * by [serializer][KSerializer] in its [KSerializer.serialize] method, the type of the token directly matches
+ * the [kind][SerialDescriptor.kind].
+ *
+ * This method is allowed to invoke only as the part of the whole deserialization process of the class,
+ * calling this method after invoking [beginStructure] or any `decode*` method will lead to unspecified behaviour.
+ * For example:
+ * ```
+ * class Holder(val value: Int, val list: List<Int>())
+ *
+ * // Holder deserialize method
+ * fun deserialize(decoder: Decoder): Holder {
+ * // Completely okay, the whole Holder object is read
+ * val jsonObject = (decoder as JsonDecoder).decodeJsonElement()
+ * // ...
+ * }
+ *
+ * // Incorrect Holder deserialize method
+ * fun deserialize(decoder: Decoder): Holder {
+ * // decode "value" key unconditionally
+ * decoder.decodeElementIndex(descriptor)
+ * val value = decode.decodeInt()
+ * // Incorrect, decoder is already in an intermediate state after decodeInt
+ * val json = (decoder as JsonDecoder).decodeJsonElement()
+ * // ...
+ * }
+ * ```
+ */
+ public fun decodeJsonElement(): JsonElement
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt
new file mode 100644
index 00000000..7d213304
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("unused")
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.internal.*
+
+/**
+ * Class representing single JSON element.
+ * Can be [JsonPrimitive], [JsonArray] or [JsonObject].
+ *
+ * [JsonElement.toString] properly prints JSON tree as valid JSON, taking into account quoted values and primitives.
+ * Whole hierarchy is serializable, but only when used with [Json] as [JsonElement] is purely JSON-specific structure
+ * which has a meaningful schemaless semantics only for JSON.
+ *
+ * The whole hierarchy is [serializable][Serializable] only by [Json] format.
+ */
+@Serializable(JsonElementSerializer::class)
+public sealed class JsonElement
+
+/**
+ * Class representing JSON primitive value.
+ * JSON primitives include numbers, strings, booleans and special null value [JsonNull].
+ */
+@Serializable(JsonPrimitiveSerializer::class)
+public sealed class JsonPrimitive : JsonElement() {
+
+ /**
+ * Indicates whether the primitive was explicitly constructed from [String] and
+ * whether it should be serialized as one. E.g. `JsonPrimitive("42")` is represented
+ * by a string, while `JsonPrimitive(42)` is not.
+ * These primitives will be serialized as `42` and `"42"` respectively.
+ */
+ public abstract val isString: Boolean
+
+ /**
+ * Content of given element without quotes. For [JsonNull] this methods returns `null`
+ */
+ public abstract val content: String
+
+ public override fun toString(): String = content
+}
+
+/**
+ * Creates [JsonPrimitive] from the given boolean.
+ */
+public fun JsonPrimitive(value: Boolean?): JsonPrimitive {
+ if (value == null) return JsonNull
+ return JsonLiteral(value, isString = false)
+}
+
+/**
+ * Creates [JsonPrimitive] from the given number.
+ */
+public fun JsonPrimitive(value: Number?): JsonPrimitive {
+ if (value == null) return JsonNull
+ return JsonLiteral(value, isString = false)
+}
+
+/**
+ * Creates [JsonPrimitive] from the given string.
+ */
+public fun JsonPrimitive(value: String?): JsonPrimitive {
+ if (value == null) return JsonNull
+ return JsonLiteral(value, isString = true)
+}
+
+// JsonLiteral is deprecated for public use and no longer available. Please use JsonPrimitive instead
+internal class JsonLiteral internal constructor(
+ body: Any,
+ public override val isString: Boolean
+) : JsonPrimitive() {
+ public override val content: String = body.toString()
+
+ public override fun toString(): String =
+ if (isString) buildString { printQuoted(content) }
+ else content
+
+ // Compare by `content` and `isString`, because body can be kotlin.Long=42 or kotlin.String="42"
+ public override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+ other as JsonLiteral
+ if (isString != other.isString) return false
+ if (content != other.content) return false
+ return true
+ }
+
+ public override fun hashCode(): Int {
+ var result = isString.hashCode()
+ result = 31 * result + content.hashCode()
+ return result
+ }
+}
+
+/**
+ * Class representing JSON `null` value
+ */
+@Serializable(JsonNullSerializer::class)
+public object JsonNull : JsonPrimitive() {
+ override val isString: Boolean get() = false
+ override val content: String = "null"
+}
+
+/**
+ * Class representing JSON object, consisting of name-value pairs, where value is arbitrary [JsonElement]
+ *
+ * Since this class also implements [Map] interface, you can use
+ * traditional methods like [Map.get] or [Map.getValue] to obtain Json elements.
+ */
+@Serializable(JsonObjectSerializer::class)
+public class JsonObject(private val content: Map<String, JsonElement>) : JsonElement(), Map<String, JsonElement> by content {
+ public override fun equals(other: Any?): Boolean = content == other
+ public override fun hashCode(): Int = content.hashCode()
+ public override fun toString(): String {
+ return content.entries.joinToString(
+ separator = ",",
+ prefix = "{",
+ postfix = "}",
+ transform = { (k, v) ->
+ buildString {
+ printQuoted(k)
+ append(':')
+ append(v)
+ }
+ }
+ )
+ }
+}
+
+/**
+ * Class representing JSON array, consisting of indexed values, where value is arbitrary [JsonElement]
+ *
+ * Since this class also implements [List] interface, you can use
+ * traditional methods like [List.get] or [List.getOrNull] to obtain Json elements.
+ */
+@Serializable(JsonArraySerializer::class)
+public class JsonArray(private val content: List<JsonElement>) : JsonElement(), List<JsonElement> by content {
+ public override fun equals(other: Any?): Boolean = content == other
+ public override fun hashCode(): Int = content.hashCode()
+ public override fun toString(): String = content.joinToString(prefix = "[", postfix = "]", separator = ",")
+}
+
+/**
+ * Convenience method to get current element as [JsonPrimitive]
+ * @throws IllegalArgumentException if current element is not a [JsonPrimitive]
+ */
+public val JsonElement.jsonPrimitive: JsonPrimitive
+ get() = this as? JsonPrimitive ?: error("JsonPrimitive")
+
+/**
+ * Convenience method to get current element as [JsonObject]
+ * @throws IllegalArgumentException if current element is not a [JsonObject]
+ */
+public val JsonElement.jsonObject: JsonObject
+ get() = this as? JsonObject ?: error("JsonObject")
+
+/**
+ * Convenience method to get current element as [JsonArray]
+ * @throws IllegalArgumentException if current element is not a [JsonArray]
+ */
+public val JsonElement.jsonArray: JsonArray
+ get() = this as? JsonArray ?: error("JsonArray")
+
+/**
+ * Convenience method to get current element as [JsonNull]
+ * @throws IllegalArgumentException if current element is not a [JsonNull]
+ */
+public val JsonElement.jsonNull: JsonNull
+ get() = this as? JsonNull ?: error("JsonNull")
+
+/**
+ * Returns content of the current element as int
+ * @throws NumberFormatException if current element is not a valid representation of number
+ */
+public val JsonPrimitive.int: Int get() = content.toInt()
+
+/**
+ * Returns content of the current element as int or `null` if current element is not a valid representation of number
+ */
+public val JsonPrimitive.intOrNull: Int? get() = content.toIntOrNull()
+
+/**
+ * Returns content of current element as long
+ * @throws NumberFormatException if current element is not a valid representation of number
+ */
+public val JsonPrimitive.long: Long get() = content.toLong()
+
+/**
+ * Returns content of current element as long or `null` if current element is not a valid representation of number
+ */
+public val JsonPrimitive.longOrNull: Long? get() = content.toLongOrNull()
+
+/**
+ * Returns content of current element as double
+ * @throws NumberFormatException if current element is not a valid representation of number
+ */
+public val JsonPrimitive.double: Double get() = content.toDouble()
+
+/**
+ * Returns content of current element as double or `null` if current element is not a valid representation of number
+ */
+public val JsonPrimitive.doubleOrNull: Double? get() = content.toDoubleOrNull()
+
+/**
+ * Returns content of current element as float
+ * @throws NumberFormatException if current element is not a valid representation of number
+ */
+public val JsonPrimitive.float: Float get() = content.toFloat()
+
+/**
+ * Returns content of current element as float or `null` if current element is not a valid representation of number
+ */
+public val JsonPrimitive.floatOrNull: Float? get() = content.toFloatOrNull()
+
+/**
+ * Returns content of current element as boolean
+ * @throws IllegalStateException if current element doesn't represent boolean
+ */
+public val JsonPrimitive.boolean: Boolean get() = content.toBooleanStrictOrNull() ?: throw IllegalStateException("$this does not represent a Boolean")
+
+/**
+ * Returns content of current element as boolean or `null` if current element is not a valid representation of boolean
+ */
+public val JsonPrimitive.booleanOrNull: Boolean? get() = content.toBooleanStrictOrNull()
+
+/**
+ * Content of the given element without quotes or `null` if current element is [JsonNull]
+ */
+public val JsonPrimitive.contentOrNull: String? get() = if (this is JsonNull) null else content
+
+private fun JsonElement.error(element: String): Nothing =
+ throw IllegalArgumentException("Element ${this::class} is not a $element")
+
+@PublishedApi
+internal fun unexpectedJson(key: String, expected: String): Nothing =
+ throw IllegalArgumentException("Element $key is not a $expected")
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElementBuilders.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElementBuilders.kt
new file mode 100644
index 00000000..c4b925f2
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElementBuilders.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalContracts::class)
+
+package kotlinx.serialization.json
+
+import kotlin.contracts.*
+
+/**
+ * Builds [JsonObject] with the given [builderAction] builder.
+ * Example of usage:
+ * ```
+ * val json = buildJsonObject {
+ * put("booleanKey", true)
+ * putJsonArray("arrayKey") {
+ * for (i in 1..10) add(i)
+ * }
+ * putJsonObject("objectKey") {
+ * put("stringKey", "stringValue")
+ * }
+ * }
+ * ```
+ */
+public inline fun buildJsonObject(builderAction: JsonObjectBuilder.() -> Unit): JsonObject {
+ contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
+ val builder = JsonObjectBuilder()
+ builder.builderAction()
+ return builder.build()
+}
+
+
+/**
+ * Builds [JsonArray] with the given [builderAction] builder.
+ * Example of usage:
+ * ```
+ * val json = buildJsonArray {
+ * add(true)
+ * addJsonArray {
+ * for (i in 1..10) add(i)
+ * }
+ * addJsonObject {
+ * put("stringKey", "stringValue")
+ * }
+ * }
+ * ```
+ */
+public inline fun buildJsonArray(builderAction: JsonArrayBuilder.() -> Unit): JsonArray {
+ contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
+ val builder = JsonArrayBuilder()
+ builder.builderAction()
+ return builder.build()
+}
+
+/**
+ * DSL builder for a [JsonObject]. To create an instance of builder, use [buildJsonObject] build function.
+ */
+@JsonDslMarker
+public class JsonObjectBuilder @PublishedApi internal constructor() {
+
+ private val content: MutableMap<String, JsonElement> = linkedMapOf()
+
+ /**
+ * Add the given JSON [element] to a resulting JSON object using the given [key].
+ *
+ * Returns the previous value associated with [key], or `null` if the key was not present.
+ */
+ public fun put(key: String, element: JsonElement): JsonElement? = content.put(key, element)
+
+ @PublishedApi
+ internal fun build(): JsonObject = JsonObject(content)
+}
+
+/**
+ * Add the [JSON][JsonObject] produced by the [builderAction] function to a resulting json object using the given [key].
+ *
+ * Returns the previous value associated with [key], or `null` if the key was not present.
+ */
+public fun JsonObjectBuilder.putJsonObject(key: String, builderAction: JsonObjectBuilder.() -> Unit): JsonElement? =
+ put(key, buildJsonObject(builderAction))
+
+/**
+ * Add the [JSON array][JsonArray] produced by the [builderAction] function to a resulting json object using the given [key].
+ *
+ * Returns the previous value associated with [key], or `null` if the key was not present.
+ */
+public fun JsonObjectBuilder.putJsonArray(key: String, builderAction: JsonArrayBuilder.() -> Unit): JsonElement? =
+ put(key, buildJsonArray(builderAction))
+
+/**
+ * Add the given boolean [value] to a resulting JSON object using the given [key].
+ *
+ * Returns the previous value associated with [key], or `null` if the key was not present.
+ */
+public fun JsonObjectBuilder.put(key: String, value: Boolean?): JsonElement? = put(key, JsonPrimitive(value))
+
+/**
+ * Add the given numeric [value] to a resulting JSON object using the given [key].
+ *
+ * Returns the previous value associated with [key], or `null` if the key was not present.
+ */
+public fun JsonObjectBuilder.put(key: String, value: Number?): JsonElement? = put(key, JsonPrimitive(value))
+
+/**
+ * Add the given string [value] to a resulting JSON object using the given [key].
+ *
+ * Returns the previous value associated with [key], or `null` if the key was not present.
+ */
+public fun JsonObjectBuilder.put(key: String, value: String?): JsonElement? = put(key, JsonPrimitive(value))
+
+/**
+ * DSL builder for a [JsonArray]. To create an instance of builder, use [buildJsonArray] build function.
+ */
+@JsonDslMarker
+public class JsonArrayBuilder @PublishedApi internal constructor() {
+
+ private val content: MutableList<JsonElement> = mutableListOf()
+
+ /**
+ * Adds the given JSON [element] to a resulting array.
+ *
+ * Always returns `true` similarly to [ArrayList] specification.
+ */
+ public fun add(element: JsonElement): Boolean {
+ content += element
+ return true
+ }
+
+ @PublishedApi
+ internal fun build(): JsonArray = JsonArray(content)
+}
+
+/**
+ * Adds the given boolean [value] to a resulting array.
+ *
+ * Always returns `true` similarly to [ArrayList] specification.
+ */
+public fun JsonArrayBuilder.add(value: Boolean?): Boolean = add(JsonPrimitive(value))
+
+/**
+ * Adds the given numeric [value] to a resulting array.
+ *
+ * Always returns `true` similarly to [ArrayList] specification.
+ */
+public fun JsonArrayBuilder.add(value: Number?): Boolean = add(JsonPrimitive(value))
+
+/**
+ * Adds the given string [value] to a resulting array.
+ *
+ * Always returns `true` similarly to [ArrayList] specification.
+ */
+public fun JsonArrayBuilder.add(value: String?): Boolean = add(JsonPrimitive(value))
+
+/**
+ * Adds the [JSON][JsonObject] produced by the [builderAction] function to a resulting array.
+ *
+ * Always returns `true` similarly to [ArrayList] specification.
+ */
+public fun JsonArrayBuilder.addJsonObject(builderAction: JsonObjectBuilder.() -> Unit): Boolean =
+ add(buildJsonObject(builderAction))
+
+/**
+ * Adds the [JSON][JsonArray] produced by the [builderAction] function to a resulting array.
+ *
+ * Always returns `true` similarly to [ArrayList] specification.
+ */
+public fun JsonArrayBuilder.addJsonArray(builderAction: JsonArrayBuilder.() -> Unit): Boolean =
+ add(buildJsonArray(builderAction))
+
+private const val infixToDeprecated = "Infix 'to' operator is deprecated for removal for the favour of 'add'"
+private const val unaryPlusDeprecated = "Unary plus is deprecated for removal for the favour of 'add'"
+
+@DslMarker
+internal annotation class JsonDslMarker
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElementSerializers.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElementSerializers.kt
new file mode 100644
index 00000000..a6d0d0f4
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElementSerializers.kt
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.internal.JsonDecodingException
+
+/**
+ * External [Serializer] object providing [SerializationStrategy] and [DeserializationStrategy] for [JsonElement].
+ * It can only be used by with [Json] format an its input ([JsonDecoder] and [JsonEncoder]).
+ * Currently, this hierarchy has no guarantees on descriptor content.
+ *
+ * Example usage:
+ * ```
+ * val string = Json.encodeToString(JsonElementSerializer, json { "key" to 1.0 })
+ * val literal = Json.decodeFromString(JsonElementSerializer, string)
+ * assertEquals(JsonObject(mapOf("key" to JsonLiteral(1.0))), literal)
+ * ```
+ */
+@Serializer(forClass = JsonElement::class)
+@PublishedApi
+internal object JsonElementSerializer : KSerializer<JsonElement> {
+ override val descriptor: SerialDescriptor =
+ buildSerialDescriptor("kotlinx.serialization.json.JsonElement", PolymorphicKind.SEALED) {
+ // Resolve cyclic dependency in descriptors by late binding
+ element("JsonPrimitive", defer { JsonPrimitiveSerializer.descriptor })
+ element("JsonNull", defer { JsonNullSerializer.descriptor })
+ element("JsonLiteral", defer { JsonLiteralSerializer.descriptor })
+ element("JsonObject", defer { JsonObjectSerializer.descriptor })
+ element("JsonArray", defer { JsonArraySerializer.descriptor })
+ }
+
+ override fun serialize(encoder: Encoder, value: JsonElement) {
+ verify(encoder)
+ when (value) {
+ is JsonPrimitive -> encoder.encodeSerializableValue(JsonPrimitiveSerializer, value)
+ is JsonObject -> encoder.encodeSerializableValue(JsonObjectSerializer, value)
+ is JsonArray -> encoder.encodeSerializableValue(JsonArraySerializer, value)
+ }
+ }
+
+ override fun deserialize(decoder: Decoder): JsonElement {
+ val input = decoder.asJsonDecoder()
+ return input.decodeJsonElement()
+ }
+}
+
+/**
+ * External [Serializer] object providing [SerializationStrategy] and [DeserializationStrategy] for [JsonPrimitive].
+ * It can only be used by with [Json] format an its input ([JsonDecoder] and [JsonEncoder]).
+ */
+@Serializer(forClass = JsonPrimitive::class)
+@PublishedApi
+internal object JsonPrimitiveSerializer : KSerializer<JsonPrimitive> {
+ override val descriptor: SerialDescriptor =
+ buildSerialDescriptor("kotlinx.serialization.json.JsonPrimitive", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: JsonPrimitive) {
+ verify(encoder)
+ return if (value is JsonNull) {
+ encoder.encodeSerializableValue(JsonNullSerializer, JsonNull)
+ } else {
+ encoder.encodeSerializableValue(JsonLiteralSerializer, value as JsonLiteral)
+ }
+ }
+
+ override fun deserialize(decoder: Decoder): JsonPrimitive {
+ val result = decoder.asJsonDecoder().decodeJsonElement()
+ if (result !is JsonPrimitive) throw JsonDecodingException(-1, "Unexpected JSON element, expected JsonPrimitive, had ${result::class}", result.toString())
+ return result
+ }
+}
+
+/**
+ * External [Serializer] object providing [SerializationStrategy] and [DeserializationStrategy] for [JsonNull].
+ * It can only be used by with [Json] format an its input ([JsonDecoder] and [JsonEncoder]).
+ */
+@Serializer(forClass = JsonNull::class)
+@PublishedApi
+internal object JsonNullSerializer : KSerializer<JsonNull> {
+ // technically, JsonNull is an object, but it does not call beginStructure/endStructure at all
+ override val descriptor: SerialDescriptor =
+ buildSerialDescriptor("kotlinx.serialization.json.JsonNull", SerialKind.ENUM)
+
+ override fun serialize(encoder: Encoder, value: JsonNull) {
+ verify(encoder)
+ encoder.encodeNull()
+ }
+
+ override fun deserialize(decoder: Decoder): JsonNull {
+ verify(decoder)
+ if (decoder.decodeNotNullMark()) {
+ throw JsonDecodingException("Expected 'null' literal")
+ }
+ decoder.decodeNull()
+ return JsonNull
+ }
+}
+
+private object JsonLiteralSerializer : KSerializer<JsonLiteral> {
+
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("kotlinx.serialization.json.JsonLiteral", PrimitiveKind.STRING)
+
+ @OptIn(ExperimentalUnsignedTypes::class, ExperimentalSerializationApi::class)
+ override fun serialize(encoder: Encoder, value: JsonLiteral) {
+ verify(encoder)
+ if (value.isString) {
+ return encoder.encodeString(value.content)
+ }
+
+ value.longOrNull?.let { return encoder.encodeLong(it) }
+
+ // most unsigned values fit to .longOrNull, but not ULong
+ value.content.toULongOrNull()?.let {
+ encoder.encodeInline(ULong.serializer().descriptor).encodeLong(it.toLong())
+ return
+ }
+
+ value.doubleOrNull?.let { return encoder.encodeDouble(it) }
+ value.booleanOrNull?.let { return encoder.encodeBoolean(it) }
+
+ encoder.encodeString(value.content)
+ }
+
+ override fun deserialize(decoder: Decoder): JsonLiteral {
+ val result = decoder.asJsonDecoder().decodeJsonElement()
+ if (result !is JsonLiteral) throw JsonDecodingException(-1, "Unexpected JSON element, expected JsonLiteral, had ${result::class}", result.toString())
+ return result
+ }
+}
+
+/**
+ * External [Serializer] object providing [SerializationStrategy] and [DeserializationStrategy] for [JsonObject].
+ * It can only be used by with [Json] format an its input ([JsonDecoder] and [JsonEncoder]).
+ */
+@Serializer(forClass = JsonObject::class)
+@PublishedApi
+internal object JsonObjectSerializer : KSerializer<JsonObject> {
+
+ private object JsonObjectDescriptor : SerialDescriptor by MapSerializer(String.serializer(), JsonElementSerializer).descriptor {
+ @ExperimentalSerializationApi
+ override val serialName: String = "kotlinx.serialization.json.JsonObject"
+ }
+
+ override val descriptor: SerialDescriptor = JsonObjectDescriptor
+
+ override fun serialize(encoder: Encoder, value: JsonObject) {
+ verify(encoder)
+ MapSerializer(String.serializer(), JsonElementSerializer).serialize(encoder, value)
+ }
+
+ override fun deserialize(decoder: Decoder): JsonObject {
+ verify(decoder)
+ return JsonObject(MapSerializer(String.serializer(), JsonElementSerializer).deserialize(decoder))
+ }
+}
+
+/**
+ * External [Serializer] object providing [SerializationStrategy] and [DeserializationStrategy] for [JsonArray].
+ * It can only be used by with [Json] format an its input ([JsonDecoder] and [JsonEncoder]).
+ */
+@Serializer(forClass = JsonArray::class)
+@PublishedApi
+internal object JsonArraySerializer : KSerializer<JsonArray> {
+
+ private object JsonArrayDescriptor : SerialDescriptor by ListSerializer(JsonElementSerializer).descriptor {
+ @ExperimentalSerializationApi
+ override val serialName: String = "kotlinx.serialization.json.JsonArray"
+ }
+
+ override val descriptor: SerialDescriptor = JsonArrayDescriptor
+
+ override fun serialize(encoder: Encoder, value: JsonArray) {
+ verify(encoder)
+ ListSerializer(JsonElementSerializer).serialize(encoder, value)
+ }
+
+ override fun deserialize(decoder: Decoder): JsonArray {
+ verify(decoder)
+ return JsonArray(ListSerializer(JsonElementSerializer).deserialize(decoder))
+ }
+}
+
+private fun verify(encoder: Encoder) {
+ encoder.asJsonEncoder()
+}
+
+private fun verify(decoder: Decoder) {
+ decoder.asJsonDecoder()
+}
+
+internal fun Decoder.asJsonDecoder(): JsonDecoder = this as? JsonDecoder
+ ?: throw IllegalStateException(
+ "This serializer can be used only with Json format." +
+ "Expected Decoder to be JsonDecoder, got ${this::class}"
+ )
+
+internal fun Encoder.asJsonEncoder() = this as? JsonEncoder
+ ?: throw IllegalStateException(
+ "This serializer can be used only with Json format." +
+ "Expected Encoder to be JsonEncoder, got ${this::class}"
+ )
+
+
+/**
+ * Returns serial descriptor that delegates all the calls to descriptor returned by [deferred] block.
+ * Used to resolve cyclic dependencies between recursive serializable structures.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+private fun defer(deferred: () -> SerialDescriptor): SerialDescriptor = object : SerialDescriptor {
+
+ private val original: SerialDescriptor by lazy(deferred)
+
+ override val serialName: String
+ get() = original.serialName
+ override val kind: SerialKind
+ get() = original.kind
+ override val elementsCount: Int
+ get() = original.elementsCount
+
+ override fun getElementName(index: Int): String = original.getElementName(index)
+ override fun getElementIndex(name: String): Int = original.getElementIndex(name)
+ override fun getElementAnnotations(index: Int): List<Annotation> = original.getElementAnnotations(index)
+ override fun getElementDescriptor(index: Int): SerialDescriptor = original.getElementDescriptor(index)
+ override fun isElementOptional(index: Int): Boolean = original.isElementOptional(index)
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonEncoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonEncoder.kt
new file mode 100644
index 00000000..3dd57b9f
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonEncoder.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.encoding.*
+
+/**
+ * Encoder used by [Json] during serialization.
+ * This interface can be used to inject desired behaviour into a serialization process of [Json].
+ *
+ * Typical example of the usage:
+ * ```
+ * // Class representing Either<Left|Right>
+ * sealed class Either {
+ * data class Left(val errorMsg: String) : Either()
+ * data class Right(val data: Payload) : Either()
+ * }
+ *
+ * // Serializer injects custom behaviour by inspecting object content and writing
+ * object EitherSerializer : KSerializer<Either> {
+ * override val descriptor: SerialDescriptor = buildSerialDescriptor("package.Either", PolymorphicKind.SEALED) {
+ * // ..
+ * }
+ *
+ * override fun deserialize(decoder: Decoder): Either {
+ * val input = decoder as? JsonDecoder ?: throw SerializationException("This class can be decoded only by Json format")
+ * val tree = input.decodeJsonElement() as? JsonObject ?: throw SerializationException("Expected JsonObject")
+ * if ("error" in tree) return Either.Left(tree["error"]!!.jsonPrimitive.content)
+ * return Either.Right(input.json.decodeFromJsonElement(Payload.serializer(), tree))
+ * }
+ *
+ * override fun serialize(encoder: Encoder, value: Either) {
+ * val output = encoder as? JsonEncoder ?: throw SerializationException("This class can be encoded only by Json format")
+ * val tree = when (value) {
+ * is Either.Left -> JsonObject(mapOf("error" to JsonPrimitve(value.errorMsg)))
+ * is Either.Right -> output.json.encodeToJsonElement(Payload.serializer(), value.data)
+ * }
+ * output.encodeJsonElement(tree)
+ * }
+ * }
+ * ```
+ *
+ * ### Not stable for inheritance
+ *
+ * `JsonEncoder` interface is not stable for inheritance in 3rd party libraries, as new methods
+ * might be added to this interface or contracts of the existing methods can be changed.
+ * Accepting this interface in your API methods, casting [Encoder] to [JsonEncoder] and invoking its
+ * methods is considered stable.
+ */
+public interface JsonEncoder : Encoder, CompositeEncoder {
+ /**
+ * An instance of the current [Json].
+ */
+ public val json: Json
+
+ /**
+ * Appends the given JSON [element] to the current output.
+ * This method is allowed to invoke only as the part of the whole serialization process of the class,
+ * calling this method after invoking [beginStructure] or any `encode*` method will lead to unspecified behaviour
+ * and may produce an invalid JSON result.
+ * For example:
+ * ```
+ * class Holder(val value: Int, val list: List<Int>())
+ *
+ * // Holder serialize method
+ * fun serialize(encoder: Encoder, value: Holder) {
+ * // Completely okay, the whole Holder object is read
+ * val jsonObject = JsonObject(...) // build a JsonObject from Holder
+ * (encoder as JsonEncoder).encodeJsonElement(jsonObject) // Write it
+ * }
+ *
+ * // Incorrect Holder serialize method
+ * fun serialize(encoder: Encoder, value: Holder) {
+ * val composite = encoder.beginStructure(descriptor)
+ * composite.encodeSerializableElement(descriptor, 0, Int.serializer(), value.value)
+ * val array = JsonArray(value.list)
+ * // Incorrect, encoder is already in an intermediate state after encodeSerializableElement
+ * (composite as JsonEncoder).encodeJsonElement(array)
+ * composite.endStructure(descriptor)
+ * // ...
+ * }
+ * ```
+ */
+ public fun encodeJsonElement(element: JsonElement)
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonTransformingSerializer.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonTransformingSerializer.kt
new file mode 100644
index 00000000..8d1485d6
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonTransformingSerializer.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("DeprecatedCallableAddReplaceWith")
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.internal.*
+
+/**
+ * Base class for custom serializers that allows manipulating an abstract JSON
+ * representation of the class before serialization or deserialization.
+ *
+ * [JsonTransformingSerializer] provides capabilities to manipulate [JsonElement] representation
+ * directly instead of interacting with [Encoder] and [Decoder] in order to apply a custom
+ * transformation to the JSON.
+ * Please note that this class expects that [Encoder] and [Decoder] are implemented by [JsonDecoder] and [JsonEncoder],
+ * i.e. serializers derived from this class work only with [Json] format.
+ *
+ * There are two methods in which JSON transformation can be defined: [transformSerialize] and [transformDeserialize].
+ * You can override one or both of them. Consult their documentation for details.
+ *
+ * Usage example:
+ *
+ * ```
+ * @Serializable
+ * data class Example(
+ * @Serializable(UnwrappingJsonListSerializer::class) val data: String
+ * )
+ * // Unwraps a list to a single object
+ * object UnwrappingJsonListSerializer :
+ * JsonTransformingSerializer<String>(String.serializer()) {
+ * override fun transformDeserialize(element: JsonElement): JsonElement {
+ * if (element !is JsonArray) return element
+ * require(element.size == 1) { "Array size must be equal to 1 to unwrap it" }
+ * return element.first()
+ * }
+ * }
+ * // Now these functions both yield correct result:
+ * Json.parse(Example.serializer(), """{"data":["str1"]}""")
+ * Json.parse(Example.serializer(), """{"data":"str1"}""")
+ * ```
+ *
+ * @param T A type for Kotlin property for which this serializer could be applied.
+ * **Not** the type that you may encounter in JSON. (e.g. if you unwrap a list
+ * to a single value `T`, use `T`, not `List<T>`)
+ * @param tSerializer A serializer for type [T]. Determines [JsonElement] which is passed to [transformSerialize].
+ * Should be able to parse [JsonElement] from [transformDeserialize] function.
+ * Usually, default [serializer] is sufficient.
+ */
+public abstract class JsonTransformingSerializer<T : Any>(
+ private val tSerializer: KSerializer<T>
+) : KSerializer<T> {
+
+ /**
+ * A descriptor for this transformation.
+ * By default, it delegates to [tSerializer]'s descriptor.
+ *
+ * However, this descriptor can be overridden to achieve better representation of the resulting JSON shape
+ * for schema generating or introspection purposes.
+ */
+ override val descriptor: SerialDescriptor get() = tSerializer.descriptor
+
+ final override fun serialize(encoder: Encoder, value: T) {
+ val output = encoder.asJsonEncoder()
+ var element = output.json.writeJson(value, tSerializer)
+ element = transformSerialize(element)
+ output.encodeJsonElement(element)
+ }
+
+ final override fun deserialize(decoder: Decoder): T {
+ val input = decoder.asJsonDecoder()
+ val element = input.decodeJsonElement()
+ return input.json.decodeFromJsonElement(tSerializer, transformDeserialize(element))
+ }
+
+ /**
+ * Transformation that happens during [deserialize] call.
+ * Does nothing by default.
+ *
+ * During deserialization, a value from JSON is firstly decoded to a [JsonElement],
+ * user transformation in [transformDeserialize] is applied,
+ * and then resulting [JsonElement] is deserialized to [T] with [tSerializer].
+ */
+ protected open fun transformDeserialize(element: JsonElement): JsonElement = element
+
+ /**
+ * Transformation that happens during [serialize] call.
+ * Does nothing by default.
+ *
+ * During serialization, a value of type [T] is serialized with [tSerializer] to a [JsonElement],
+ * user transformation in [transformSerialize] is applied, and then resulting [JsonElement] is encoded to a JSON string.
+ */
+ protected open fun transformSerialize(element: JsonElement): JsonElement = element
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/Composers.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/Composers.kt
new file mode 100644
index 00000000..2bc080f9
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/Composers.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:OptIn(ExperimentalSerializationApi::class)
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.json.Json
+import kotlin.jvm.JvmField
+
+internal fun Composer(sb: JsonStringBuilder, json: Json): Composer =
+ if (json.configuration.prettyPrint) ComposerWithPrettyPrint(sb, json) else Composer(sb)
+
+@OptIn(ExperimentalSerializationApi::class)
+internal open class Composer(@JvmField internal val sb: JsonStringBuilder) {
+ var writingFirst = true
+ protected set
+
+ open fun indent() {
+ writingFirst = true
+ }
+
+ open fun unIndent() = Unit
+
+ open fun nextItem() {
+ writingFirst = false
+ }
+
+ open fun space() = Unit
+
+ fun print(v: Char) = sb.append(v)
+ fun print(v: String) = sb.append(v)
+ open fun print(v: Float) = sb.append(v.toString())
+ open fun print(v: Double) = sb.append(v.toString())
+ open fun print(v: Byte) = sb.append(v.toLong())
+ open fun print(v: Short) = sb.append(v.toLong())
+ open fun print(v: Int) = sb.append(v.toLong())
+ open fun print(v: Long) = sb.append(v)
+ open fun print(v: Boolean) = sb.append(v.toString())
+ fun printQuoted(value: String): Unit = sb.appendQuoted(value)
+}
+
+@ExperimentalUnsignedTypes
+internal class ComposerForUnsignedNumbers(sb: JsonStringBuilder) : Composer(sb) {
+ override fun print(v: Int) {
+ return super.print(v.toUInt().toString())
+ }
+
+ override fun print(v: Long) {
+ return super.print(v.toULong().toString())
+ }
+
+ override fun print(v: Byte) {
+ return super.print(v.toUByte().toString())
+ }
+
+ override fun print(v: Short) {
+ return super.print(v.toUShort().toString())
+ }
+}
+
+internal class ComposerWithPrettyPrint(
+ sb: JsonStringBuilder,
+ private val json: Json
+) : Composer(sb) {
+ private var level = 0
+
+ override fun indent() {
+ writingFirst = true
+ level++
+ }
+
+ override fun unIndent() {
+ level--
+ }
+
+ override fun nextItem() {
+ writingFirst = false
+ print("\n")
+ repeat(level) { print(json.configuration.prettyPrintIndent) }
+ }
+
+ override fun space() {
+ print(' ')
+ }
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonConfiguration.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonConfiguration.kt
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonConfiguration.kt
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonElementMarker.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonElementMarker.kt
new file mode 100644
index 00000000..2535739c
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonElementMarker.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.internal.ElementMarker
+
+@OptIn(ExperimentalSerializationApi::class)
+internal class JsonElementMarker(descriptor: SerialDescriptor) {
+ private val origin: ElementMarker = ElementMarker(descriptor, ::readIfAbsent)
+
+ internal var isUnmarkedNull: Boolean = false
+ private set
+
+ internal fun mark(index: Int) {
+ origin.mark(index)
+ }
+
+ internal fun nextUnmarkedIndex(): Int {
+ return origin.nextUnmarkedIndex()
+ }
+
+ private fun readIfAbsent(descriptor: SerialDescriptor, index: Int): Boolean {
+ isUnmarkedNull = !descriptor.isElementOptional(index) && descriptor.getElementDescriptor(index).isNullable
+ return isUnmarkedNull
+ }
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonExceptions.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonExceptions.kt
new file mode 100644
index 00000000..d1698db2
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonExceptions.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("FunctionName")
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.json.*
+
+/**
+ * Generic exception indicating a problem with JSON serialization and deserialization.
+ */
+internal open class JsonException(message: String) : SerializationException(message)
+
+/**
+ * Thrown when [Json] has failed to parse the given JSON string or deserialize it to a target class.
+ */
+internal class JsonDecodingException(message: String) : JsonException(message)
+
+internal fun JsonDecodingException(offset: Int, message: String) =
+ JsonDecodingException(if (offset >= 0) "Unexpected JSON token at offset $offset: $message" else message)
+
+/**
+ * Thrown when [Json] has failed to create a JSON string from the given value.
+ */
+internal class JsonEncodingException(message: String) : JsonException(message)
+
+internal fun JsonDecodingException(offset: Int, message: String, input: CharSequence) =
+ JsonDecodingException(offset, "$message\nJSON input: ${input.minify(offset)}")
+
+internal fun InvalidFloatingPointEncoded(value: Number, output: String) = JsonEncodingException(
+ "Unexpected special floating-point value $value. By default, " +
+ "non-finite floating point values are prohibited because they do not conform JSON specification. " +
+ "$specialFlowingValuesHint\n" +
+ "Current output: ${output.minify()}"
+)
+
+
+// Extension on JSON reader and fail immediately
+internal fun AbstractJsonLexer.throwInvalidFloatingPointDecoded(result: Number): Nothing {
+ fail("Unexpected special floating-point value $result. By default, " +
+ "non-finite floating point values are prohibited because they do not conform JSON specification",
+ hint = specialFlowingValuesHint)
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal fun InvalidKeyKindException(keyDescriptor: SerialDescriptor) = JsonEncodingException(
+ "Value of type '${keyDescriptor.serialName}' can't be used in JSON as a key in the map. " +
+ "It should have either primitive or enum kind, but its kind is '${keyDescriptor.kind}'.\n" +
+ allowStructuredMapKeysHint
+)
+
+// Exceptions for tree-based decoder
+
+internal fun InvalidFloatingPointEncoded(value: Number, key: String, output: String) =
+ JsonEncodingException(unexpectedFpErrorMessage(value, key, output))
+
+internal fun InvalidFloatingPointDecoded(value: Number, key: String, output: String) =
+ JsonDecodingException(-1, unexpectedFpErrorMessage(value, key, output))
+
+private fun unexpectedFpErrorMessage(value: Number, key: String, output: String): String {
+ return "Unexpected special floating-point value $value with key $key. By default, " +
+ "non-finite floating point values are prohibited because they do not conform JSON specification. " +
+ "$specialFlowingValuesHint\n" +
+ "Current output: ${output.minify()}"
+}
+
+internal fun UnknownKeyException(key: String, input: String) = JsonDecodingException(
+ -1,
+ "Encountered unknown key '$key'.\n" +
+ "$ignoreUnknownKeysHint\n" +
+ "Current input: ${input.minify()}"
+)
+
+private fun CharSequence.minify(offset: Int = -1): CharSequence {
+ if (length < 200) return this
+ if (offset == -1) {
+ val start = this.length - 60
+ if (start <= 0) return this
+ return "....." + substring(start)
+ }
+
+ val start = offset - 30
+ val end = offset + 30
+ val prefix = if (start <= 0) "" else "....."
+ val suffix = if (end >= length) "" else "....."
+ return prefix + substring(start.coerceAtLeast(0), end.coerceAtMost(length)) + suffix
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonNamesMap.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonNamesMap.kt
new file mode 100644
index 00000000..93e604a7
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonNamesMap.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlin.native.concurrent.*
+
+@SharedImmutable
+internal val JsonAlternativeNamesKey = DescriptorSchemaCache.Key<Map<String, Int>>()
+
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.buildAlternativeNamesMap(): Map<String, Int> {
+ fun MutableMap<String, Int>.putOrThrow(name: String, index: Int) {
+ if (name in this) {
+ throw JsonException(
+ "The suggested name '$name' for property ${getElementName(index)} is already one of the names for property " +
+ "${getElementName(getValue(name))} in ${this@buildAlternativeNamesMap}"
+ )
+ }
+ this[name] = index
+ }
+
+ var builder: MutableMap<String, Int>? = null
+ for (i in 0 until elementsCount) {
+ getElementAnnotations(i).filterIsInstance<JsonNames>().singleOrNull()?.names?.forEach { name ->
+ if (builder == null) builder = createMapForCache(elementsCount)
+ builder!!.putOrThrow(name, i)
+ }
+ }
+ return builder ?: emptyMap()
+}
+
+/**
+ * Serves same purpose as [SerialDescriptor.getElementIndex] but respects
+ * [JsonNames] annotation and [JsonConfiguration.useAlternativeNames] state.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.getJsonNameIndex(json: Json, name: String): Int {
+ val index = getElementIndex(name)
+ // Fast path, do not go through ConcurrentHashMap.get
+ // Note, it blocks ability to detect collisions between the primary name and alternate,
+ // but it eliminates a significant performance penalty (about -15% without this optimization)
+ if (index != CompositeDecoder.UNKNOWN_NAME) return index
+ if (!json.configuration.useAlternativeNames) return index
+ // Slow path
+ val alternativeNamesMap =
+ json.schemaCache.getOrPut(this, JsonAlternativeNamesKey, this::buildAlternativeNamesMap)
+ return alternativeNamesMap[name] ?: CompositeDecoder.UNKNOWN_NAME
+}
+
+/**
+ * Throws on [CompositeDecoder.UNKNOWN_NAME]
+ */
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.getJsonNameIndexOrThrow(json: Json, name: String, suffix: String = ""): Int {
+ val index = getJsonNameIndex(json, name)
+ if (index == CompositeDecoder.UNKNOWN_NAME)
+ throw SerializationException("$serialName does not contain element with name '$name'$suffix")
+ return index
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal inline fun Json.tryCoerceValue(
+ elementDescriptor: SerialDescriptor,
+ peekNull: () -> Boolean,
+ peekString: () -> String?,
+ onEnumCoercing: () -> Unit = {}
+): Boolean {
+ if (!elementDescriptor.isNullable && peekNull()) return true
+ if (elementDescriptor.kind == SerialKind.ENUM) {
+ val enumValue = peekString()
+ ?: return false // if value is not a string, decodeEnum() will throw correct exception
+ val enumIndex = elementDescriptor.getJsonNameIndex(this, enumValue)
+ if (enumIndex == CompositeDecoder.UNKNOWN_NAME) {
+ onEnumCoercing()
+ return true
+ }
+ }
+ return false
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt
new file mode 100644
index 00000000..4e055b23
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt
@@ -0,0 +1,141 @@
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.internal.*
+
+/**
+ * Internal representation of the current JSON path.
+ * It is stored as the array of serial descriptors (for regular classes)
+ * and `Any?` in case of Map keys.
+ *
+ * Example of the state when decoding the list
+ * ```
+ * class Foo(val a: Int, val l: List<String>)
+ *
+ * // {"l": ["a", "b", "c"] }
+ *
+ * Current path when decoding array elements:
+ * Foo.descriptor, List(String).descriptor
+ * 1 (index of the 'l'), 2 (index of currently being decoded "c")
+ * ```
+ */
+internal class JsonPath {
+
+ // Tombstone indicates that we are within a map, but the map key is currently being decoded.
+ // It is also used to overwrite a previous map key to avoid memory leaks and misattribution.
+ object Tombstone
+
+ /*
+ * Serial descriptor, map key or the tombstone for map key
+ */
+ private var currentObjectPath = arrayOfNulls<Any?>(8)
+ /*
+ * Index is a small state-machine used to determine the state of the path:
+ * >=0 -> index of the element being decoded with the outer class currentObjectPath[currentDepth]
+ * -1 -> nested elements are not yet decoded
+ * -2 -> the map is being decoded and both its descriptor AND the last key were added to the path.
+ *
+ * -2 is effectively required to specify that two slots has been claimed and both should be
+ * cleaned up when the decoding is done.
+ * The cleanup is essential in order to avoid memory leaks for huge strings and structured keys.
+ */
+ private var indicies = IntArray(8) { -1 }
+ private var currentDepth = -1
+
+ // Invoked when class is started being decoded
+ fun pushDescriptor(sd: SerialDescriptor) {
+ val depth = ++currentDepth
+ if (depth == currentObjectPath.size) {
+ resize()
+ }
+ currentObjectPath[depth] = sd
+ }
+
+ // Invoked when index-th element of the current descriptor is being decoded
+ fun updateDescriptorIndex(index: Int) {
+ indicies[currentDepth] = index
+ }
+
+ /*
+ * For maps we cannot use indicies and should use the key as an element of the path instead.
+ * The key can be even an object (e.g. in a case of 'allowStructuredMapKeys') where
+ * 'toString' is way too heavy or have side-effects.
+ * For that we are storing the key instead.
+ */
+ fun updateCurrentMapKey(key: Any?) {
+ // idx != -2 -> this is the very first key being added
+ if (indicies[currentDepth] != -2 && ++currentDepth == currentObjectPath.size) {
+ resize()
+ }
+ currentObjectPath[currentDepth] = key
+ indicies[currentDepth] = -2
+ }
+
+ /** Used to indicate that we are in the process of decoding the key itself and can't specify it in path */
+ fun resetCurrentMapKey() {
+ if (indicies[currentDepth] == -2) {
+ currentObjectPath[currentDepth] = Tombstone
+ }
+ }
+
+ fun popDescriptor() {
+ // When we are ending map, we pop the last key and the outer field as well
+ val depth = currentDepth
+ if (indicies[depth] == -2) {
+ indicies[depth] = -1
+ currentDepth--
+ }
+ // Guard against top-level maps
+ if (currentDepth != -1) {
+ // No need to clean idx up as it was already cleaned by updateDescriptorIndex(DECODE_DONE)
+ currentDepth--
+ }
+ }
+
+ @OptIn(ExperimentalSerializationApi::class)
+ fun getPath(): String {
+ return buildString {
+ append("$")
+ repeat(currentDepth + 1) {
+ val element = currentObjectPath[it]
+ if (element is SerialDescriptor) {
+ if (element.kind == StructureKind.LIST) {
+ if (indicies[it] != -1) {
+ append("[")
+ append(indicies[it])
+ append("]")
+ }
+ } else {
+ val idx = indicies[it]
+ // If an actual element is being decoded
+ if (idx >= 0) {
+ append(".")
+ append(element.getElementName(idx))
+ }
+ }
+ } else if (element !== Tombstone) {
+ append("[")
+ // All non-indicies should be properly quoted by JsonPath convention
+ append("'")
+ // Else -- map key
+ append(element)
+ append("'")
+ append("]")
+ }
+ }
+ }
+ }
+
+
+ @OptIn(ExperimentalSerializationApi::class)
+ private fun prettyString(it: Any?) = (it as? SerialDescriptor)?.serialName ?: it.toString()
+
+ private fun resize() {
+ val newSize = currentDepth * 2
+ currentObjectPath = currentObjectPath.copyOf(newSize)
+ indicies = indicies.copyOf(newSize)
+ }
+
+ override fun toString(): String = getPath()
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt
new file mode 100644
index 00000000..f9245d58
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt
@@ -0,0 +1,10 @@
+package kotlinx.serialization.json.internal
+
+internal expect class JsonStringBuilder constructor() {
+ fun append(value: Long)
+ fun append(ch: Char)
+ fun append(string: String)
+ fun appendQuoted(string: String)
+ override fun toString(): String
+ fun release()
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonTreeReader.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonTreeReader.kt
new file mode 100644
index 00000000..7c01daa8
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonTreeReader.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.json.*
+
+@OptIn(ExperimentalSerializationApi::class)
+internal class JsonTreeReader(
+ configuration: JsonConfiguration,
+ private val lexer: AbstractJsonLexer
+) {
+ private val isLenient = configuration.isLenient
+ private var stackDepth = 0
+
+ private fun readObject(): JsonElement = readObjectImpl {
+ read()
+ }
+
+ private suspend fun DeepRecursiveScope<Unit, JsonElement>.readObject(): JsonElement =
+ readObjectImpl { callRecursive(Unit) }
+
+ private inline fun readObjectImpl(reader: () -> JsonElement): JsonObject {
+ var lastToken = lexer.consumeNextToken(TC_BEGIN_OBJ)
+ if (lexer.peekNextToken() == TC_COMMA) lexer.fail("Unexpected leading comma")
+ val result = linkedMapOf<String, JsonElement>()
+ while (lexer.canConsumeValue()) {
+ // Read key and value
+ val key = if (isLenient) lexer.consumeStringLenient() else lexer.consumeString()
+ lexer.consumeNextToken(TC_COLON)
+ val element = reader()
+ result[key] = element
+ // Verify the next token
+ lastToken = lexer.consumeNextToken()
+ when (lastToken) {
+ TC_COMMA -> Unit // no-op, can continue with `canConsumeValue` that verifies the token after comma
+ TC_END_OBJ -> break // `canConsumeValue` can return incorrect result, since it checks token _after_ TC_END_OBJ
+ else -> lexer.fail("Expected end of the object or comma")
+ }
+ }
+ // Check for the correct ending
+ if (lastToken == TC_BEGIN_OBJ) { // Case of empty object
+ lexer.consumeNextToken(TC_END_OBJ)
+ } else if (lastToken == TC_COMMA) { // Trailing comma
+ lexer.fail("Unexpected trailing comma")
+ }
+ return JsonObject(result)
+ }
+
+ private fun readArray(): JsonElement {
+ var lastToken = lexer.consumeNextToken()
+ // Prohibit leading comma
+ if (lexer.peekNextToken() == TC_COMMA) lexer.fail("Unexpected leading comma")
+ val result = arrayListOf<JsonElement>()
+ while (lexer.canConsumeValue()) {
+ val element = read()
+ result.add(element)
+ lastToken = lexer.consumeNextToken()
+ if (lastToken != TC_COMMA) {
+ lexer.require(lastToken == TC_END_LIST) { "Expected end of the array or comma" }
+ }
+ }
+ // Check for the correct ending
+ if (lastToken == TC_BEGIN_LIST) { // Case of empty object
+ lexer.consumeNextToken(TC_END_LIST)
+ } else if (lastToken == TC_COMMA) { // Trailing comma
+ lexer.fail("Unexpected trailing comma")
+ }
+ return JsonArray(result)
+ }
+
+ private fun readValue(isString: Boolean): JsonPrimitive {
+ val string = if (isLenient || !isString) {
+ lexer.consumeStringLenient()
+ } else {
+ lexer.consumeString()
+ }
+ if (!isString && string == NULL) return JsonNull
+ return JsonLiteral(string, isString)
+ }
+
+ fun read(): JsonElement {
+ return when (val token = lexer.peekNextToken()) {
+ TC_STRING -> readValue(isString = true)
+ TC_OTHER -> readValue(isString = false)
+ TC_BEGIN_OBJ -> {
+ /*
+ * If the object has the depth of 200 (an arbitrary "good enough" constant), it means
+ * that it's time to switch to stackless recursion to avoid StackOverflowError.
+ * This case is quite rare and specific, so more complex nestings (e.g. through
+ * the chain of JsonArray and JsonElement) are not supported.
+ */
+ val result = if (++stackDepth == 200) {
+ readDeepRecursive()
+ } else {
+ readObject()
+ }
+ --stackDepth
+ result
+ }
+ TC_BEGIN_LIST -> readArray()
+ else -> lexer.fail("Cannot begin reading element, unexpected token: $token")
+ }
+ }
+
+ private fun readDeepRecursive(): JsonElement = DeepRecursiveFunction<Unit, JsonElement> {
+ when (lexer.peekNextToken()) {
+ TC_STRING -> readValue(isString = true)
+ TC_OTHER -> readValue(isString = false)
+ TC_BEGIN_OBJ -> readObject()
+ TC_BEGIN_LIST -> readArray()
+ else -> lexer.fail("Can't begin reading element, unexpected token")
+ }
+ }.invoke(Unit)
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt
new file mode 100644
index 00000000..ea65c48a
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.json.*
+
+@Suppress("UNCHECKED_CAST")
+internal inline fun <T> JsonEncoder.encodePolymorphically(
+ serializer: SerializationStrategy<T>,
+ value: T,
+ ifPolymorphic: (String) -> Unit
+) {
+ if (serializer !is AbstractPolymorphicSerializer<*> || json.configuration.useArrayPolymorphism) {
+ serializer.serialize(this, value)
+ return
+ }
+ val casted = serializer as AbstractPolymorphicSerializer<Any>
+ val baseClassDiscriminator = serializer.descriptor.classDiscriminator(json)
+ val actualSerializer = casted.findPolymorphicSerializer(this, value as Any)
+ validateIfSealed(casted, actualSerializer, baseClassDiscriminator)
+ checkKind(actualSerializer.descriptor.kind)
+ ifPolymorphic(baseClassDiscriminator)
+ actualSerializer.serialize(this, value)
+}
+
+private fun validateIfSealed(
+ serializer: SerializationStrategy<*>,
+ actualSerializer: SerializationStrategy<Any>,
+ classDiscriminator: String
+) {
+ if (serializer !is SealedClassSerializer<*>) return
+ @Suppress("DEPRECATION_ERROR")
+ if (classDiscriminator in actualSerializer.descriptor.jsonCachedSerialNames()) {
+ val baseName = serializer.descriptor.serialName
+ val actualName = actualSerializer.descriptor.serialName
+ error(
+ "Sealed class '$actualName' cannot be serialized as base class '$baseName' because" +
+ " it has property name that conflicts with JSON class discriminator '$classDiscriminator'. " +
+ "You can either change class discriminator in JsonConfiguration, " +
+ "rename property with @SerialName annotation or fall back to array polymorphism"
+ )
+ }
+}
+
+internal fun checkKind(kind: SerialKind) {
+ if (kind is SerialKind.ENUM) error("Enums cannot be serialized polymorphically with 'type' parameter. You can use 'JsonBuilder.useArrayPolymorphism' instead")
+ if (kind is PrimitiveKind) error("Primitives cannot be serialized polymorphically with 'type' parameter. You can use 'JsonBuilder.useArrayPolymorphism' instead")
+ if (kind is PolymorphicKind) error("Actual serializer for polymorphic cannot be polymorphic itself")
+}
+
+internal fun <T> JsonDecoder.decodeSerializableValuePolymorphic(deserializer: DeserializationStrategy<T>): T {
+ if (deserializer !is AbstractPolymorphicSerializer<*> || json.configuration.useArrayPolymorphism) {
+ return deserializer.deserialize(this)
+ }
+
+ val jsonTree = cast<JsonObject>(decodeJsonElement(), deserializer.descriptor)
+ val discriminator = deserializer.descriptor.classDiscriminator(json)
+ val type = jsonTree[discriminator]?.jsonPrimitive?.content
+ val actualSerializer = deserializer.findPolymorphicSerializerOrNull(this, type)
+ ?: throwSerializerNotFound(type, jsonTree)
+
+ @Suppress("UNCHECKED_CAST")
+ return json.readPolymorphicJson(discriminator, jsonTree, actualSerializer as DeserializationStrategy<T>)
+}
+
+private fun throwSerializerNotFound(type: String?, jsonTree: JsonObject): Nothing {
+ val suffix =
+ if (type == null) "missing class discriminator ('null')"
+ else "class discriminator '$type'"
+ throw JsonDecodingException(-1, "Polymorphic serializer was not found for $suffix", jsonTree.toString())
+}
+
+internal fun SerialDescriptor.classDiscriminator(json: Json): String {
+ // Plain loop is faster than allocation of Sequence or ArrayList
+ // We can rely on the fact that only one JsonClassDiscriminator is present —
+ // compiler plugin checked that.
+ for (annotation in annotations) {
+ if (annotation is JsonClassDiscriminator) return annotation.discriminator
+ }
+ return json.configuration.classDiscriminator
+}
+
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/PolymorphismValidator.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/PolymorphismValidator.kt
new file mode 100644
index 00000000..01994f75
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/PolymorphismValidator.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.modules.*
+import kotlin.reflect.*
+
+@OptIn(ExperimentalSerializationApi::class)
+internal class PolymorphismValidator(
+ private val useArrayPolymorphism: Boolean,
+ private val discriminator: String
+) : SerializersModuleCollector {
+
+ override fun <T : Any> contextual(
+ kClass: KClass<T>,
+ provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>
+ ) {
+ // Nothing here
+ }
+
+ override fun <Base : Any, Sub : Base> polymorphic(
+ baseClass: KClass<Base>,
+ actualClass: KClass<Sub>,
+ actualSerializer: KSerializer<Sub>
+ ) {
+ val descriptor = actualSerializer.descriptor
+ checkKind(descriptor, actualClass)
+ if (!useArrayPolymorphism) {
+ // Collisions with "type" can happen only for JSON polymorphism
+ checkDiscriminatorCollisions(descriptor, actualClass)
+ }
+ }
+
+ private fun checkKind(descriptor: SerialDescriptor, actualClass: KClass<*>) {
+ val kind = descriptor.kind
+ if (kind is PolymorphicKind || kind == SerialKind.CONTEXTUAL) {
+ throw IllegalArgumentException("Serializer for ${actualClass.simpleName} can't be registered as a subclass for polymorphic serialization " +
+ "because its kind $kind is not concrete. To work with multiple hierarchies, register it as a base class.")
+ }
+
+ if (useArrayPolymorphism) return
+ /*
+ * For this kind we can't intercept the JSON object {} in order to add "type: ...".
+ * Except for maps that just can clash and accidentally overwrite the type.
+ */
+ if (kind == StructureKind.LIST || kind == StructureKind.MAP
+ || kind is PrimitiveKind
+ || kind is SerialKind.ENUM
+ ) {
+ throw IllegalArgumentException(
+ "Serializer for ${actualClass.simpleName} of kind $kind cannot be serialized polymorphically with class discriminator."
+ )
+ }
+ }
+
+ private fun checkDiscriminatorCollisions(
+ descriptor: SerialDescriptor,
+ actualClass: KClass<*>
+ ) {
+ for (i in 0 until descriptor.elementsCount) {
+ val name = descriptor.getElementName(i)
+ if (name == discriminator) {
+ throw IllegalArgumentException(
+ "Polymorphic serializer for $actualClass has property '$name' that conflicts " +
+ "with JSON class discriminator. You can either change class discriminator in JsonConfiguration, " +
+ "rename property with @SerialName annotation " +
+ "or fall back to array polymorphism"
+ )
+ }
+ }
+ }
+
+ override fun <Base : Any> polymorphicDefaultSerializer(
+ baseClass: KClass<Base>,
+ defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?
+ ) {
+ // Nothing here
+ }
+
+ override fun <Base : Any> polymorphicDefaultDeserializer(
+ baseClass: KClass<Base>,
+ defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<out Base>?
+ ) {
+ // Nothing here
+ }
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/SchemaCache.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/SchemaCache.kt
new file mode 100644
index 00000000..de65fb68
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/SchemaCache.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.descriptors.*
+import kotlin.native.concurrent.*
+
+private typealias DescriptorData<T> = MutableMap<DescriptorSchemaCache.Key<T>, T>
+
+/**
+ * A type-safe map for storing custom information (such as format schema), associated with [SerialDescriptor].
+ *
+ * This cache uses ConcurrentHashMap on JVM and regular maps on other platforms.
+ * To be able to work with it from multiple threads in Kotlin/Native, use @[ThreadLocal] in appropriate places.
+ */
+internal class DescriptorSchemaCache {
+ private val map: MutableMap<SerialDescriptor, DescriptorData<Any>> = createMapForCache(1)
+
+ @Suppress("UNCHECKED_CAST")
+ public operator fun <T : Any> set(descriptor: SerialDescriptor, key: Key<T>, value: T) {
+ map.getOrPut(descriptor, { createMapForCache(1) })[key as Key<Any>] = value as Any
+ }
+
+ public fun <T : Any> getOrPut(descriptor: SerialDescriptor, key: Key<T>, defaultValue: () -> T): T {
+ get(descriptor, key)?.let { return it }
+ val value = defaultValue()
+ set(descriptor, key, value)
+ return value
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ public operator fun <T : Any> get(descriptor: SerialDescriptor, key: Key<T>): T? {
+ return map[descriptor]?.get(key as Key<Any>) as? T
+ }
+
+ /**
+ * A key for associating user data of type [T] with a given [SerialDescriptor].
+ */
+ public class Key<T : Any> {}
+}
+
+
+/**
+ * Creates a ConcurrentHashMap on JVM and regular HashMap on other platforms.
+ * To make actual use of cache in Kotlin/Native, mark a top-level object with this map
+ * as a @[ThreadLocal].
+ */
+internal expect fun <K, V> createMapForCache(initialCapacity: Int): MutableMap<K, V>
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt
new file mode 100644
index 00000000..bf229044
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.encoding.CompositeDecoder.Companion.DECODE_DONE
+import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.jvm.*
+
+/**
+ * [JsonDecoder] which reads given JSON from [AbstractJsonLexer] field by field.
+ */
+@OptIn(ExperimentalSerializationApi::class, ExperimentalUnsignedTypes::class)
+internal open class StreamingJsonDecoder(
+ final override val json: Json,
+ private val mode: WriteMode,
+ @JvmField internal val lexer: AbstractJsonLexer,
+ descriptor: SerialDescriptor
+) : JsonDecoder, AbstractDecoder() {
+
+ override val serializersModule: SerializersModule = json.serializersModule
+ private var currentIndex = -1
+ private val configuration = json.configuration
+
+ private val elementMarker: JsonElementMarker? = if (configuration.explicitNulls) null else JsonElementMarker(descriptor)
+
+ override fun decodeJsonElement(): JsonElement = JsonTreeReader(json.configuration, lexer).read()
+
+ @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+ override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
+ try {
+ return decodeSerializableValuePolymorphic(deserializer)
+ } catch (e: MissingFieldException) {
+ throw MissingFieldException(e.message + " at path: " + lexer.path.getPath(), e)
+ }
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ val newMode = json.switchMode(descriptor)
+ lexer.path.pushDescriptor(descriptor)
+ lexer.consumeNextToken(newMode.begin)
+ checkLeadingComma()
+ return when (newMode) {
+ // In fact resets current index that these modes rely on
+ WriteMode.LIST, WriteMode.MAP, WriteMode.POLY_OBJ -> StreamingJsonDecoder(
+ json,
+ newMode,
+ lexer,
+ descriptor
+ )
+ else -> if (mode == newMode && json.configuration.explicitNulls) {
+ this
+ } else {
+ StreamingJsonDecoder(json, newMode, lexer, descriptor)
+ }
+ }
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ // If we're ignoring unknown keys, we have to skip all undecoded elements,
+ // e.g. for object serialization. It can be the case when the descriptor does
+ // not have any elements and decodeElementIndex is not invoked at all
+ if (json.configuration.ignoreUnknownKeys && descriptor.elementsCount == 0) {
+ skipLeftoverElements(descriptor)
+ }
+ // First consume the object so we know it's correct
+ lexer.consumeNextToken(mode.end)
+ // Then cleanup the path
+ lexer.path.popDescriptor()
+ }
+
+ private fun skipLeftoverElements(descriptor: SerialDescriptor) {
+ while (decodeElementIndex(descriptor) != DECODE_DONE) {
+ // Skip elements
+ }
+ }
+
+ override fun decodeNotNullMark(): Boolean {
+ return !(elementMarker?.isUnmarkedNull ?: false) && lexer.tryConsumeNotNull()
+ }
+
+ override fun decodeNull(): Nothing? {
+ // Do nothing, null was consumed by `decodeNotNullMark`
+ return null
+ }
+
+ private fun checkLeadingComma() {
+ if (lexer.peekNextToken() == TC_COMMA) {
+ lexer.fail("Unexpected leading comma")
+ }
+ }
+
+ override fun <T> decodeSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ deserializer: DeserializationStrategy<T>,
+ previousValue: T?
+ ): T {
+ val isMapKey = mode == WriteMode.MAP && index and 1 == 0
+ // Reset previous key
+ if (isMapKey) {
+ lexer.path.resetCurrentMapKey()
+ }
+ // Deserialize the key
+ val value = super.decodeSerializableElement(descriptor, index, deserializer, previousValue)
+ // Put the key to the path
+ if (isMapKey) {
+ lexer.path.updateCurrentMapKey(value)
+ }
+ return value
+ }
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ val index = when (mode) {
+ WriteMode.OBJ -> decodeObjectIndex(descriptor)
+ WriteMode.MAP -> decodeMapIndex()
+ else -> decodeListIndex() // Both for LIST and default polymorphic
+ }
+ // The element of the next index that will be decoded
+ if (mode != WriteMode.MAP) {
+ lexer.path.updateDescriptorIndex(index)
+ }
+ return index
+ }
+
+ private fun decodeMapIndex(): Int {
+ var hasComma = false
+ val decodingKey = currentIndex % 2 != 0
+ if (decodingKey) {
+ if (currentIndex != -1) {
+ hasComma = lexer.tryConsumeComma()
+ }
+ } else {
+ lexer.consumeNextToken(COLON)
+ }
+
+ return if (lexer.canConsumeValue()) {
+ if (decodingKey) {
+ if (currentIndex == -1) lexer.require(!hasComma) { "Unexpected trailing comma" }
+ else lexer.require(hasComma) { "Expected comma after the key-value pair" }
+ }
+ ++currentIndex
+ } else {
+ if (hasComma) lexer.fail("Expected '}', but had ',' instead")
+ CompositeDecoder.DECODE_DONE
+ }
+ }
+
+ /*
+ * Checks whether JSON has `null` value for non-null property or unknown enum value for enum property
+ */
+ private fun coerceInputValue(descriptor: SerialDescriptor, index: Int): Boolean = json.tryCoerceValue(
+ descriptor.getElementDescriptor(index),
+ { !lexer.tryConsumeNotNull() },
+ { lexer.peekString(configuration.isLenient) },
+ { lexer.consumeString() /* skip unknown enum string*/ }
+ )
+
+ @Suppress("INVISIBLE_MEMBER")
+ private fun decodeObjectIndex(descriptor: SerialDescriptor): Int {
+ // hasComma checks are required to properly react on trailing commas
+ var hasComma = lexer.tryConsumeComma()
+ while (lexer.canConsumeValue()) { // TODO: consider merging comma consumption and this check
+ hasComma = false
+ val key = decodeStringKey()
+ lexer.consumeNextToken(COLON)
+ val index = descriptor.getJsonNameIndex(json, key)
+ val isUnknown = if (index != UNKNOWN_NAME) {
+ if (configuration.coerceInputValues && coerceInputValue(descriptor, index)) {
+ hasComma = lexer.tryConsumeComma()
+ false // Known element, but coerced
+ } else {
+ elementMarker?.mark(index)
+ return index // Known element without coercing, return it
+ }
+ } else {
+ true // unknown element
+ }
+
+ if (isUnknown) { // slow-path for unknown keys handling
+ hasComma = handleUnknown(key)
+ }
+ }
+ if (hasComma) lexer.fail("Unexpected trailing comma")
+
+ return elementMarker?.nextUnmarkedIndex() ?: CompositeDecoder.DECODE_DONE
+ }
+
+ private fun handleUnknown(key: String): Boolean {
+ if (configuration.ignoreUnknownKeys) {
+ lexer.skipElement(configuration.isLenient)
+ } else {
+ // Here we cannot properly update json path indicies
+ // as we do not have a proper SerialDecriptor in our hands
+ lexer.failOnUnknownKey(key)
+ }
+ return lexer.tryConsumeComma()
+ }
+
+ private fun decodeListIndex(): Int {
+ // Prohibit leading comma
+ val hasComma = lexer.tryConsumeComma()
+ return if (lexer.canConsumeValue()) {
+ if (currentIndex != -1 && !hasComma) lexer.fail("Expected end of the array or comma")
+ ++currentIndex
+ } else {
+ if (hasComma) lexer.fail("Unexpected trailing comma")
+ CompositeDecoder.DECODE_DONE
+ }
+ }
+
+
+ override fun decodeBoolean(): Boolean {
+ /*
+ * We prohibit non true/false boolean literals at all as it is considered way too error-prone,
+ * but allow quoted literal in relaxed mode for booleans.
+ */
+ return if (configuration.isLenient) {
+ lexer.consumeBooleanLenient()
+ } else {
+ lexer.consumeBoolean()
+ }
+ }
+
+ /*
+ * The rest of the primitives are allowed to be quoted and unquoted
+ * to simplify integrations with third-party API.
+ */
+ override fun decodeByte(): Byte {
+ val value = lexer.consumeNumericLiteral()
+ // Check for overflow
+ if (value != value.toByte().toLong()) lexer.fail("Failed to parse byte for input '$value'")
+ return value.toByte()
+ }
+
+ override fun decodeShort(): Short {
+ val value = lexer.consumeNumericLiteral()
+ // Check for overflow
+ if (value != value.toShort().toLong()) lexer.fail("Failed to parse short for input '$value'")
+ return value.toShort()
+ }
+
+ override fun decodeInt(): Int {
+ val value = lexer.consumeNumericLiteral()
+ // Check for overflow
+ if (value != value.toInt().toLong()) lexer.fail("Failed to parse int for input '$value'")
+ return value.toInt()
+ }
+
+ override fun decodeLong(): Long {
+ return lexer.consumeNumericLiteral()
+ }
+
+ override fun decodeFloat(): Float {
+ val result = lexer.parseString("float") { toFloat() }
+ val specialFp = json.configuration.allowSpecialFloatingPointValues
+ if (specialFp || result.isFinite()) return result
+ lexer.throwInvalidFloatingPointDecoded(result)
+ }
+
+ override fun decodeDouble(): Double {
+ val result = lexer.parseString("double") { toDouble() }
+ val specialFp = json.configuration.allowSpecialFloatingPointValues
+ if (specialFp || result.isFinite()) return result
+ lexer.throwInvalidFloatingPointDecoded(result)
+ }
+
+ override fun decodeChar(): Char {
+ val string = lexer.consumeStringLenient()
+ if (string.length != 1) lexer.fail("Expected single char, but got '$string'")
+ return string[0]
+ }
+
+ private fun decodeStringKey(): String {
+ return if (configuration.isLenient) {
+ lexer.consumeStringLenientNotNull()
+ } else {
+ lexer.consumeKeyString()
+ }
+ }
+
+ override fun decodeString(): String {
+ return if (configuration.isLenient) {
+ lexer.consumeStringLenientNotNull()
+ } else {
+ lexer.consumeString()
+ }
+ }
+
+ override fun decodeInline(inlineDescriptor: SerialDescriptor): Decoder =
+ if (inlineDescriptor.isUnsignedNumber) JsonDecoderForUnsignedTypes(lexer, json)
+ else super.decodeInline(inlineDescriptor)
+
+ override fun decodeEnum(enumDescriptor: SerialDescriptor): Int {
+ return enumDescriptor.getJsonNameIndexOrThrow(json, decodeString(), " at path " + lexer.path.getPath())
+ }
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+@ExperimentalUnsignedTypes
+internal class JsonDecoderForUnsignedTypes(
+ private val lexer: AbstractJsonLexer,
+ json: Json
+) : AbstractDecoder() {
+ override val serializersModule: SerializersModule = json.serializersModule
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int = error("unsupported")
+
+ override fun decodeInt(): Int = lexer.parseString("UInt") { toUInt().toInt() }
+ override fun decodeLong(): Long = lexer.parseString("ULong") { toULong().toLong() }
+ override fun decodeByte(): Byte = lexer.parseString("UByte") { toUByte().toByte() }
+ override fun decodeShort(): Short = lexer.parseString("UShort") { toUShort().toShort() }
+}
+
+private inline fun <T> AbstractJsonLexer.parseString(expectedType: String, block: String.() -> T): T {
+ val input = consumeStringLenient()
+ try {
+ return input.block()
+ } catch (e: IllegalArgumentException) {
+ fail("Failed to parse type '$expectedType' for input '$input'")
+ }
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt
new file mode 100644
index 00000000..cdaeeb03
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.native.concurrent.*
+
+@ExperimentalSerializationApi
+@OptIn(ExperimentalUnsignedTypes::class)
+@SharedImmutable
+private val unsignedNumberDescriptors = setOf(
+ UInt.serializer().descriptor,
+ ULong.serializer().descriptor,
+ UByte.serializer().descriptor,
+ UShort.serializer().descriptor
+)
+
+@ExperimentalSerializationApi
+internal val SerialDescriptor.isUnsignedNumber: Boolean
+ get() = this.isInline && this in unsignedNumberDescriptors
+
+@OptIn(ExperimentalSerializationApi::class, ExperimentalUnsignedTypes::class)
+internal class StreamingJsonEncoder(
+ private val composer: Composer,
+ override val json: Json,
+ private val mode: WriteMode,
+ private val modeReuseCache: Array<JsonEncoder?>?
+) : JsonEncoder, AbstractEncoder() {
+
+ internal constructor(
+ output: JsonStringBuilder, json: Json, mode: WriteMode,
+ modeReuseCache: Array<JsonEncoder?>
+ ) : this(Composer(output, json), json, mode, modeReuseCache)
+
+ override val serializersModule: SerializersModule = json.serializersModule
+ private val configuration = json.configuration
+
+ // Forces serializer to wrap all values into quotes
+ private var forceQuoting: Boolean = false
+ private var polymorphicDiscriminator: String? = null
+
+ init {
+ val i = mode.ordinal
+ if (modeReuseCache != null) {
+ if (modeReuseCache[i] !== null || modeReuseCache[i] !== this)
+ modeReuseCache[i] = this
+ }
+ }
+
+ override fun encodeJsonElement(element: JsonElement) {
+ encodeSerializableValue(JsonElementSerializer, element)
+ }
+
+ override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean {
+ return configuration.encodeDefaults
+ }
+
+ override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
+ encodePolymorphically(serializer, value) {
+ polymorphicDiscriminator = it
+ }
+ }
+
+ private fun encodeTypeInfo(descriptor: SerialDescriptor) {
+ composer.nextItem()
+ encodeString(polymorphicDiscriminator!!)
+ composer.print(COLON)
+ composer.space()
+ encodeString(descriptor.serialName)
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+ val newMode = json.switchMode(descriptor)
+ if (newMode.begin != INVALID) { // entry
+ composer.print(newMode.begin)
+ composer.indent()
+ }
+
+ if (polymorphicDiscriminator != null) {
+ encodeTypeInfo(descriptor)
+ polymorphicDiscriminator = null
+ }
+
+ if (mode == newMode) {
+ return this
+ }
+
+ return modeReuseCache?.get(newMode.ordinal) ?: StreamingJsonEncoder(composer, json, newMode, modeReuseCache)
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ if (mode.end != INVALID) {
+ composer.unIndent()
+ composer.nextItem()
+ composer.print(mode.end)
+ }
+ }
+
+ override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
+ when (mode) {
+ WriteMode.LIST -> {
+ if (!composer.writingFirst)
+ composer.print(COMMA)
+ composer.nextItem()
+ }
+ WriteMode.MAP -> {
+ if (!composer.writingFirst) {
+ forceQuoting = if (index % 2 == 0) {
+ composer.print(COMMA)
+ composer.nextItem() // indent should only be put after commas in map
+ true
+ } else {
+ composer.print(COLON)
+ composer.space()
+ false
+ }
+ } else {
+ forceQuoting = true
+ composer.nextItem()
+ }
+ }
+ WriteMode.POLY_OBJ -> {
+ if (index == 0)
+ forceQuoting = true
+ if (index == 1) {
+ composer.print(COMMA)
+ composer.space()
+ forceQuoting = false
+ }
+ }
+ else -> {
+ if (!composer.writingFirst)
+ composer.print(COMMA)
+ composer.nextItem()
+ encodeString(descriptor.getElementName(index))
+ composer.print(COLON)
+ composer.space()
+ }
+ }
+ return true
+ }
+
+ override fun <T : Any> encodeNullableSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ serializer: SerializationStrategy<T>,
+ value: T?
+ ) {
+ if (value != null || configuration.explicitNulls) {
+ super.encodeNullableSerializableElement(descriptor, index, serializer, value)
+ }
+ }
+
+ override fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder =
+ if (inlineDescriptor.isUnsignedNumber) StreamingJsonEncoder(
+ ComposerForUnsignedNumbers(composer.sb), json, mode, null
+ )
+ else super.encodeInline(inlineDescriptor)
+
+ override fun encodeNull() {
+ composer.print(NULL)
+ }
+
+ override fun encodeBoolean(value: Boolean) {
+ if (forceQuoting) encodeString(value.toString()) else composer.print(value)
+ }
+
+ override fun encodeByte(value: Byte) {
+ if (forceQuoting) encodeString(value.toString()) else composer.print(value)
+ }
+
+ override fun encodeShort(value: Short) {
+ if (forceQuoting) encodeString(value.toString()) else composer.print(value)
+ }
+
+ override fun encodeInt(value: Int) {
+ if (forceQuoting) encodeString(value.toString()) else composer.print(value)
+ }
+
+ override fun encodeLong(value: Long) {
+ if (forceQuoting) encodeString(value.toString()) else composer.print(value)
+ }
+
+ override fun encodeFloat(value: Float) {
+ // First encode value, then check, to have a prettier error message
+ if (forceQuoting) encodeString(value.toString()) else composer.print(value)
+ if (!configuration.allowSpecialFloatingPointValues && !value.isFinite()) {
+ throw InvalidFloatingPointEncoded(value, composer.sb.toString())
+ }
+ }
+
+ override fun encodeDouble(value: Double) {
+ // First encode value, then check, to have a prettier error message
+ if (forceQuoting) encodeString(value.toString()) else composer.print(value)
+ if (!configuration.allowSpecialFloatingPointValues && !value.isFinite()) {
+ throw InvalidFloatingPointEncoded(value, composer.sb.toString())
+ }
+ }
+
+ override fun encodeChar(value: Char) {
+ encodeString(value.toString())
+ }
+
+ override fun encodeString(value: String) = composer.printQuoted(value)
+
+ override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) {
+ encodeString(enumDescriptor.getElementName(index))
+ }
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt
new file mode 100644
index 00000000..99aed338
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import kotlin.native.concurrent.*
+
+private fun toHexChar(i: Int) : Char {
+ val d = i and 0xf
+ return if (d < 10) (d + '0'.code).toChar()
+ else (d - 10 + 'a'.code).toChar()
+}
+
+@SharedImmutable
+internal val ESCAPE_STRINGS: Array<String?> = arrayOfNulls<String>(93).apply {
+ for (c in 0..0x1f) {
+ val c1 = toHexChar(c shr 12)
+ val c2 = toHexChar(c shr 8)
+ val c3 = toHexChar(c shr 4)
+ val c4 = toHexChar(c)
+ this[c] = "\\u$c1$c2$c3$c4"
+ }
+ this['"'.code] = "\\\""
+ this['\\'.code] = "\\\\"
+ this['\t'.code] = "\\t"
+ this['\b'.code] = "\\b"
+ this['\n'.code] = "\\n"
+ this['\r'.code] = "\\r"
+ this[0x0c] = "\\f"
+}
+
+@SharedImmutable
+internal val ESCAPE_MARKERS: ByteArray = ByteArray(93).apply {
+ for (c in 0..0x1f) {
+ this[c] = 1.toByte()
+ }
+ this['"'.code] = '"'.code.toByte()
+ this['\\'.code] = '\\'.code.toByte()
+ this['\t'.code] = 't'.code.toByte()
+ this['\b'.code] = 'b'.code.toByte()
+ this['\n'.code] = 'n'.code.toByte()
+ this['\r'.code] = 'r'.code.toByte()
+ this[0x0c] = 'f'.code.toByte()
+}
+
+internal fun StringBuilder.printQuoted(value: String) {
+ append(STRING)
+ var lastPos = 0
+ for (i in value.indices) {
+ val c = value[i].code
+ if (c < ESCAPE_STRINGS.size && ESCAPE_STRINGS[c] != null) {
+ append(value, lastPos, i) // flush prev
+ append(ESCAPE_STRINGS[c])
+ lastPos = i + 1
+ }
+ }
+
+ if (lastPos != 0) append(value, lastPos, value.length)
+ else append(value)
+ append(STRING)
+}
+
+/**
+ * Returns `true` if the contents of this string is equal to the word "true", ignoring case, `false` if content equals "false",
+ * and returns `null` otherwise.
+ */
+internal fun String.toBooleanStrictOrNull(): Boolean? = when {
+ this.equals("true", ignoreCase = true) -> true
+ this.equals("false", ignoreCase = true) -> false
+ else -> null
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt
new file mode 100644
index 00000000..55e23a13
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("LeakingThis")
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.jvm.*
+
+internal fun <T> Json.readJson(element: JsonElement, deserializer: DeserializationStrategy<T>): T {
+ val input = when (element) {
+ is JsonObject -> JsonTreeDecoder(this, element)
+ is JsonArray -> JsonTreeListDecoder(this, element)
+ is JsonLiteral, JsonNull -> JsonPrimitiveDecoder(this, element as JsonPrimitive)
+ }
+ return input.decodeSerializableValue(deserializer)
+}
+
+internal fun <T> Json.readPolymorphicJson(
+ discriminator: String,
+ element: JsonObject,
+ deserializer: DeserializationStrategy<T>
+): T {
+ return JsonTreeDecoder(this, element, discriminator, deserializer.descriptor).decodeSerializableValue(deserializer)
+}
+
+private sealed class AbstractJsonTreeDecoder(
+ override val json: Json,
+ open val value: JsonElement
+) : NamedValueDecoder(), JsonDecoder {
+
+ override val serializersModule: SerializersModule
+ get() = json.serializersModule
+
+ @JvmField
+ protected val configuration = json.configuration
+
+ private fun currentObject() = currentTagOrNull?.let { currentElement(it) } ?: value
+
+ override fun decodeJsonElement(): JsonElement = currentObject()
+
+ override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
+ return decodeSerializableValuePolymorphic(deserializer)
+ }
+
+ override fun composeName(parentName: String, childName: String): String = childName
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ val currentObject = currentObject()
+ return when (descriptor.kind) {
+ StructureKind.LIST, is PolymorphicKind -> JsonTreeListDecoder(json, cast(currentObject, descriptor))
+ StructureKind.MAP -> json.selectMapMode(
+ descriptor,
+ { JsonTreeMapDecoder(json, cast(currentObject, descriptor)) },
+ { JsonTreeListDecoder(json, cast(currentObject, descriptor)) }
+ )
+ else -> JsonTreeDecoder(json, cast(currentObject, descriptor))
+ }
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ // Nothing
+ }
+
+ override fun decodeNotNullMark(): Boolean = currentObject() !is JsonNull
+
+ protected fun getPrimitiveValue(tag: String): JsonPrimitive {
+ val currentElement = currentElement(tag)
+ return currentElement as? JsonPrimitive ?: throw JsonDecodingException(
+ -1,
+ "Expected JsonPrimitive at $tag, found $currentElement", currentObject().toString()
+ )
+ }
+
+ protected abstract fun currentElement(tag: String): JsonElement
+
+ override fun decodeTaggedEnum(tag: String, enumDescriptor: SerialDescriptor): Int =
+ enumDescriptor.getJsonNameIndexOrThrow(json, getPrimitiveValue(tag).content)
+
+ override fun decodeTaggedNull(tag: String): Nothing? = null
+
+ override fun decodeTaggedNotNullMark(tag: String): Boolean = currentElement(tag) !== JsonNull
+
+ override fun decodeTaggedBoolean(tag: String): Boolean {
+ val value = getPrimitiveValue(tag)
+ if (!json.configuration.isLenient) {
+ val literal = value.asLiteral("boolean")
+ if (literal.isString) throw JsonDecodingException(
+ -1, "Boolean literal for key '$tag' should be unquoted.\n$lenientHint", currentObject().toString()
+ )
+ }
+ return value.primitive("boolean") {
+ booleanOrNull ?: throw IllegalArgumentException() /* Will be handled by 'primitive' */
+ }
+ }
+
+ override fun decodeTaggedByte(tag: String) = getPrimitiveValue(tag).primitive("byte") {
+ val result = int
+ if (result in Byte.MIN_VALUE..Byte.MAX_VALUE) result.toByte()
+ else null
+ }
+
+ override fun decodeTaggedShort(tag: String) = getPrimitiveValue(tag).primitive("short") {
+ val result = int
+ if (result in Short.MIN_VALUE..Short.MAX_VALUE) result.toShort()
+ else null
+ }
+
+ override fun decodeTaggedInt(tag: String) = getPrimitiveValue(tag).primitive("int") { int }
+ override fun decodeTaggedLong(tag: String) = getPrimitiveValue(tag).primitive("long") { long }
+
+ override fun decodeTaggedFloat(tag: String): Float {
+ val result = getPrimitiveValue(tag).primitive("float") { float }
+ val specialFp = json.configuration.allowSpecialFloatingPointValues
+ if (specialFp || result.isFinite()) return result
+ throw InvalidFloatingPointDecoded(result, tag, currentObject().toString())
+ }
+
+ override fun decodeTaggedDouble(tag: String): Double {
+ val result = getPrimitiveValue(tag).primitive("double") { double }
+ val specialFp = json.configuration.allowSpecialFloatingPointValues
+ if (specialFp || result.isFinite()) return result
+ throw InvalidFloatingPointDecoded(result, tag, currentObject().toString())
+ }
+
+ override fun decodeTaggedChar(tag: String): Char = getPrimitiveValue(tag).primitive("char") { content.single() }
+
+ private inline fun <T: Any> JsonPrimitive.primitive(primitive: String, block: JsonPrimitive.() -> T?): T {
+ try {
+ return block() ?: unparsedPrimitive(primitive)
+ } catch (e: IllegalArgumentException) {
+ unparsedPrimitive(primitive)
+ }
+ }
+
+ private fun unparsedPrimitive(primitive: String): Nothing {
+ throw JsonDecodingException(-1, "Failed to parse '$primitive'", currentObject().toString())
+ }
+
+ override fun decodeTaggedString(tag: String): String {
+ val value = getPrimitiveValue(tag)
+ if (!json.configuration.isLenient) {
+ val literal = value.asLiteral("string")
+ if (!literal.isString) throw JsonDecodingException(
+ -1, "String literal for key '$tag' should be quoted.\n$lenientHint", currentObject().toString()
+ )
+ }
+ if (value is JsonNull) throw JsonDecodingException(-1, "Unexpected 'null' value instead of string literal", currentObject().toString())
+ return value.content
+ }
+
+ private fun JsonPrimitive.asLiteral(type: String): JsonLiteral {
+ return this as? JsonLiteral ?: throw JsonDecodingException(-1, "Unexpected 'null' when $type was expected")
+ }
+
+ @OptIn(ExperimentalUnsignedTypes::class)
+ override fun decodeTaggedInline(tag: String, inlineDescriptor: SerialDescriptor): Decoder =
+ if (inlineDescriptor.isUnsignedNumber) JsonDecoderForUnsignedTypes(StringJsonLexer(getPrimitiveValue(tag).content), json)
+ else super.decodeTaggedInline(tag, inlineDescriptor)
+}
+
+private class JsonPrimitiveDecoder(json: Json, override val value: JsonPrimitive) : AbstractJsonTreeDecoder(json, value) {
+
+ init {
+ pushTag(PRIMITIVE_TAG)
+ }
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int = 0
+
+ override fun currentElement(tag: String): JsonElement {
+ require(tag === PRIMITIVE_TAG) { "This input can only handle primitives with '$PRIMITIVE_TAG' tag" }
+ return value
+ }
+}
+
+private open class JsonTreeDecoder(
+ json: Json,
+ override val value: JsonObject,
+ private val polyDiscriminator: String? = null,
+ private val polyDescriptor: SerialDescriptor? = null
+) : AbstractJsonTreeDecoder(json, value) {
+ private var position = 0
+ private var forceNull: Boolean = false
+ /*
+ * Checks whether JSON has `null` value for non-null property or unknown enum value for enum property
+ */
+ private fun coerceInputValue(descriptor: SerialDescriptor, index: Int, tag: String): Boolean =
+ json.tryCoerceValue(
+ descriptor.getElementDescriptor(index),
+ { currentElement(tag) is JsonNull },
+ { (currentElement(tag) as? JsonPrimitive)?.contentOrNull }
+ )
+
+ @Suppress("INVISIBLE_MEMBER")
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ while (position < descriptor.elementsCount) {
+ val name = descriptor.getTag(position++)
+ val index = position - 1
+ forceNull = false
+ if ((name in value || absenceIsNull(descriptor, index))
+ && (!configuration.coerceInputValues || !coerceInputValue(descriptor, index, name))
+ ) {
+ return index
+ }
+ }
+ return CompositeDecoder.DECODE_DONE
+ }
+
+ private fun absenceIsNull(descriptor: SerialDescriptor, index: Int): Boolean {
+ forceNull = !json.configuration.explicitNulls
+ && !descriptor.isElementOptional(index) && descriptor.getElementDescriptor(index).isNullable
+ return forceNull
+ }
+
+ override fun decodeNotNullMark(): Boolean {
+ return !forceNull && super.decodeNotNullMark()
+ }
+
+ override fun elementName(desc: SerialDescriptor, index: Int): String {
+ val mainName = desc.getElementName(index)
+ if (!configuration.useAlternativeNames) return mainName
+ // Fast path, do not go through ConcurrentHashMap.get
+ // Note, it blocks ability to detect collisions between the primary name and alternate,
+ // but it eliminates a significant performance penalty (about -15% without this optimization)
+ if (mainName in value.keys) return mainName
+ // Slow path
+ val alternativeNamesMap =
+ json.schemaCache.getOrPut(desc, JsonAlternativeNamesKey, desc::buildAlternativeNamesMap)
+ val nameInObject = value.keys.find { alternativeNamesMap[it] == index }
+ return nameInObject ?: mainName
+ }
+
+ override fun currentElement(tag: String): JsonElement = value.getValue(tag)
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ /*
+ * For polymorphic serialization we'd like to avoid excessive decoder creating in
+ * beginStructure to properly preserve 'polyDiscriminator' field and filter it out.
+ */
+ if (descriptor === polyDescriptor) return this
+ return super.beginStructure(descriptor)
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ if (configuration.ignoreUnknownKeys || descriptor.kind is PolymorphicKind) return
+ // Validate keys
+ @Suppress("DEPRECATION_ERROR")
+ val names: Set<String> =
+ if (!configuration.useAlternativeNames)
+ descriptor.jsonCachedSerialNames()
+ else
+ descriptor.jsonCachedSerialNames() + json.schemaCache[descriptor, JsonAlternativeNamesKey]?.keys.orEmpty()
+
+ for (key in value.keys) {
+ if (key !in names && key != polyDiscriminator) {
+ throw UnknownKeyException(key, value.toString())
+ }
+ }
+ }
+}
+
+private class JsonTreeMapDecoder(json: Json, override val value: JsonObject) : JsonTreeDecoder(json, value) {
+ private val keys = value.keys.toList()
+ private val size: Int = keys.size * 2
+ private var position = -1
+
+ override fun elementName(desc: SerialDescriptor, index: Int): String {
+ val i = index / 2
+ return keys[i]
+ }
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ while (position < size - 1) {
+ position++
+ return position
+ }
+ return CompositeDecoder.DECODE_DONE
+ }
+
+ override fun currentElement(tag: String): JsonElement {
+ return if (position % 2 == 0) JsonPrimitive(tag) else value.getValue(tag)
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ // do nothing, maps do not have strict keys, so strict mode check is omitted
+ }
+}
+
+private class JsonTreeListDecoder(json: Json, override val value: JsonArray) : AbstractJsonTreeDecoder(json, value) {
+ private val size = value.size
+ private var currentIndex = -1
+
+ override fun elementName(desc: SerialDescriptor, index: Int): String = (index).toString()
+
+ override fun currentElement(tag: String): JsonElement {
+ return value[tag.toInt()]
+ }
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ while (currentIndex < size - 1) {
+ currentIndex++
+ return currentIndex
+ }
+ return CompositeDecoder.DECODE_DONE
+ }
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt
new file mode 100644
index 00000000..74f02bf4
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.collections.set
+import kotlin.jvm.*
+
+internal fun <T> Json.writeJson(value: T, serializer: SerializationStrategy<T>): JsonElement {
+ lateinit var result: JsonElement
+ val encoder = JsonTreeEncoder(this) { result = it }
+ encoder.encodeSerializableValue(serializer, value)
+ return result
+}
+
+@ExperimentalSerializationApi
+private sealed class AbstractJsonTreeEncoder(
+ final override val json: Json,
+ private val nodeConsumer: (JsonElement) -> Unit
+) : NamedValueEncoder(), JsonEncoder {
+
+ final override val serializersModule: SerializersModule
+ get() = json.serializersModule
+
+ @JvmField
+ protected val configuration = json.configuration
+
+ private var polymorphicDiscriminator: String? = null
+
+ override fun encodeJsonElement(element: JsonElement) {
+ encodeSerializableValue(JsonElementSerializer, element)
+ }
+
+ override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean =
+ configuration.encodeDefaults
+
+ override fun composeName(parentName: String, childName: String): String = childName
+ abstract fun putElement(key: String, element: JsonElement)
+ abstract fun getCurrent(): JsonElement
+
+
+ override fun encodeNull() {
+ val tag = currentTagOrNull ?: return nodeConsumer(JsonNull)
+ encodeTaggedNull(tag)
+ }
+
+ override fun encodeTaggedNull(tag: String) = putElement(tag, JsonNull)
+
+ override fun encodeTaggedInt(tag: String, value: Int) = putElement(tag, JsonPrimitive(value))
+ override fun encodeTaggedByte(tag: String, value: Byte) = putElement(tag, JsonPrimitive(value))
+ override fun encodeTaggedShort(tag: String, value: Short) = putElement(tag, JsonPrimitive(value))
+ override fun encodeTaggedLong(tag: String, value: Long) = putElement(tag, JsonPrimitive(value))
+
+ override fun encodeTaggedFloat(tag: String, value: Float) {
+ // First encode value, then check, to have a prettier error message
+ putElement(tag, JsonPrimitive(value))
+ if (!configuration.allowSpecialFloatingPointValues && !value.isFinite()) {
+ throw InvalidFloatingPointEncoded(value, tag, getCurrent().toString())
+ }
+ }
+
+ override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
+ // Writing non-structured data (i.e. primitives) on top-level (e.g. without any tag) requires special output
+ if (currentTagOrNull != null || !serializer.descriptor.carrierDescriptor(serializersModule).requiresTopLevelTag) {
+ encodePolymorphically(serializer, value) { polymorphicDiscriminator = it }
+ } else JsonPrimitiveEncoder(json, nodeConsumer).apply {
+ encodeSerializableValue(serializer, value)
+ endEncode(serializer.descriptor)
+ }
+ }
+
+ override fun encodeTaggedDouble(tag: String, value: Double) {
+ // First encode value, then check, to have a prettier error message
+ putElement(tag, JsonPrimitive(value))
+ if (!configuration.allowSpecialFloatingPointValues && !value.isFinite()) {
+ throw InvalidFloatingPointEncoded(value, tag, getCurrent().toString())
+ }
+ }
+
+ override fun encodeTaggedBoolean(tag: String, value: Boolean) = putElement(tag, JsonPrimitive(value))
+ override fun encodeTaggedChar(tag: String, value: Char) = putElement(tag, JsonPrimitive(value.toString()))
+ override fun encodeTaggedString(tag: String, value: String) = putElement(tag, JsonPrimitive(value))
+ override fun encodeTaggedEnum(
+ tag: String,
+ enumDescriptor: SerialDescriptor,
+ ordinal: Int
+ ) = putElement(tag, JsonPrimitive(enumDescriptor.getElementName(ordinal)))
+
+ override fun encodeTaggedValue(tag: String, value: Any) {
+ putElement(tag, JsonPrimitive(value.toString()))
+ }
+
+ @OptIn(ExperimentalUnsignedTypes::class)
+ override fun encodeTaggedInline(tag: String, inlineDescriptor: SerialDescriptor): Encoder =
+ if (inlineDescriptor.isUnsignedNumber) object : AbstractEncoder() {
+ override val serializersModule: SerializersModule = json.serializersModule
+
+ fun putUnquotedString(s: String) = putElement(tag, JsonLiteral(s, isString = false))
+ override fun encodeInt(value: Int) = putUnquotedString(value.toUInt().toString())
+ override fun encodeLong(value: Long) = putUnquotedString(value.toULong().toString())
+ override fun encodeByte(value: Byte) = putUnquotedString(value.toUByte().toString())
+ override fun encodeShort(value: Short) = putUnquotedString(value.toUShort().toString())
+ }
+ else super.encodeTaggedInline(tag, inlineDescriptor)
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+ val consumer =
+ if (currentTagOrNull == null) nodeConsumer
+ else { node -> putElement(currentTag, node) }
+
+ val encoder = when (descriptor.kind) {
+ StructureKind.LIST, is PolymorphicKind -> JsonTreeListEncoder(json, consumer)
+ StructureKind.MAP -> json.selectMapMode(
+ descriptor,
+ { JsonTreeMapEncoder(json, consumer) },
+ { JsonTreeListEncoder(json, consumer) }
+ )
+ else -> JsonTreeEncoder(json, consumer)
+ }
+
+ if (polymorphicDiscriminator != null) {
+ encoder.putElement(polymorphicDiscriminator!!, JsonPrimitive(descriptor.serialName))
+ polymorphicDiscriminator = null
+ }
+
+ return encoder
+ }
+
+ override fun endEncode(descriptor: SerialDescriptor) {
+ nodeConsumer(getCurrent())
+ }
+}
+
+private val SerialDescriptor.requiresTopLevelTag: Boolean
+ get() = kind is PrimitiveKind || kind === SerialKind.ENUM
+
+internal const val PRIMITIVE_TAG = "primitive" // also used in JsonPrimitiveInput
+
+private class JsonPrimitiveEncoder(
+ json: Json,
+ nodeConsumer: (JsonElement) -> Unit
+) : AbstractJsonTreeEncoder(json, nodeConsumer) {
+ private var content: JsonElement? = null
+
+ init {
+ pushTag(PRIMITIVE_TAG)
+ }
+
+ override fun putElement(key: String, element: JsonElement) {
+ require(key === PRIMITIVE_TAG) { "This output can only consume primitives with '$PRIMITIVE_TAG' tag" }
+ require(content == null) { "Primitive element was already recorded. Does call to .encodeXxx happen more than once?" }
+ content = element
+ }
+
+ override fun getCurrent(): JsonElement =
+ requireNotNull(content) { "Primitive element has not been recorded. Is call to .encodeXxx is missing in serializer?" }
+}
+
+private open class JsonTreeEncoder(
+ json: Json, nodeConsumer: (JsonElement) -> Unit
+) : AbstractJsonTreeEncoder(json, nodeConsumer) {
+
+ protected val content: MutableMap<String, JsonElement> = linkedMapOf()
+
+ override fun putElement(key: String, element: JsonElement) {
+ content[key] = element
+ }
+
+ override fun <T : Any> encodeNullableSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ serializer: SerializationStrategy<T>,
+ value: T?
+ ) {
+ if (value != null || configuration.explicitNulls) {
+ super.encodeNullableSerializableElement(descriptor, index, serializer, value)
+ }
+ }
+
+ override fun getCurrent(): JsonElement = JsonObject(content)
+}
+
+private class JsonTreeMapEncoder(json: Json, nodeConsumer: (JsonElement) -> Unit) : JsonTreeEncoder(json, nodeConsumer) {
+ private lateinit var tag: String
+ private var isKey = true
+
+ override fun putElement(key: String, element: JsonElement) {
+ if (isKey) { // writing key
+ tag = when (element) {
+ is JsonPrimitive -> element.content
+ is JsonObject -> throw InvalidKeyKindException(JsonObjectSerializer.descriptor)
+ is JsonArray -> throw InvalidKeyKindException(JsonArraySerializer.descriptor)
+ }
+ isKey = false
+ } else {
+ content[tag] = element
+ isKey = true
+ }
+ }
+
+ override fun getCurrent(): JsonElement {
+ return JsonObject(content)
+ }
+}
+
+private class JsonTreeListEncoder(json: Json, nodeConsumer: (JsonElement) -> Unit) :
+ AbstractJsonTreeEncoder(json, nodeConsumer) {
+ private val array: ArrayList<JsonElement> = arrayListOf()
+ override fun elementName(descriptor: SerialDescriptor, index: Int): String = index.toString()
+
+ override fun putElement(key: String, element: JsonElement) {
+ val idx = key.toInt()
+ array.add(idx, element)
+ }
+
+ override fun getCurrent(): JsonElement = JsonArray(array)
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal inline fun <reified T : JsonElement> cast(value: JsonElement, descriptor: SerialDescriptor): T {
+ if (value !is T) {
+ throw JsonDecodingException(
+ -1,
+ "Expected ${T::class} as the serialized body of ${descriptor.serialName}, but had ${value::class}"
+ )
+ }
+ return value
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/WriteMode.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/WriteMode.kt
new file mode 100644
index 00000000..382d8bce
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/WriteMode.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.jvm.*
+
+internal enum class WriteMode(@JvmField val begin: Char, @JvmField val end: Char) {
+ OBJ(BEGIN_OBJ, END_OBJ),
+ LIST(BEGIN_LIST, END_LIST),
+ MAP(BEGIN_OBJ, END_OBJ),
+ POLY_OBJ(BEGIN_LIST, END_LIST);
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+internal fun Json.switchMode(desc: SerialDescriptor): WriteMode =
+ when (desc.kind) {
+ is PolymorphicKind -> WriteMode.POLY_OBJ
+ StructureKind.LIST -> WriteMode.LIST
+ StructureKind.MAP -> selectMapMode(desc, { WriteMode.MAP }, { WriteMode.LIST })
+ else -> WriteMode.OBJ
+ }
+
+@OptIn(ExperimentalSerializationApi::class)
+internal inline fun <T, R1 : T, R2 : T> Json.selectMapMode(
+ mapDescriptor: SerialDescriptor,
+ ifMap: () -> R1,
+ ifList: () -> R2
+): T {
+ val keyDescriptor = mapDescriptor.getElementDescriptor(0).carrierDescriptor(serializersModule)
+ val keyKind = keyDescriptor.kind
+
+ return if (keyKind is PrimitiveKind || keyKind == SerialKind.ENUM) {
+ ifMap()
+ } else if (configuration.allowStructuredMapKeys) {
+ ifList()
+ } else {
+ throw InvalidKeyKindException(keyDescriptor)
+ }
+}
+
+internal fun SerialDescriptor.carrierDescriptor(module: SerializersModule): SerialDescriptor = when {
+ kind == SerialKind.CONTEXTUAL -> module.getContextualDescriptor(this)?.carrierDescriptor(module) ?: this
+ isInline -> getElementDescriptor(0).carrierDescriptor(module)
+ else -> this
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt
new file mode 100644
index 00000000..5e1eee7c
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt
@@ -0,0 +1,647 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.json.internal.CharMappings.CHAR_TO_TOKEN
+import kotlinx.serialization.json.internal.CharMappings.ESCAPE_2_CHAR
+import kotlin.js.*
+import kotlin.jvm.*
+
+internal const val lenientHint = "Use 'isLenient = true' in 'Json {}` builder to accept non-compliant JSON."
+internal const val coerceInputValuesHint = "Use 'coerceInputValues = true' in 'Json {}` builder to coerce nulls to default values."
+internal const val specialFlowingValuesHint =
+ "It is possible to deserialize them using 'JsonBuilder.allowSpecialFloatingPointValues = true'"
+internal const val ignoreUnknownKeysHint = "Use 'ignoreUnknownKeys = true' in 'Json {}' builder to ignore unknown keys."
+internal const val allowStructuredMapKeysHint =
+ "Use 'allowStructuredMapKeys = true' in 'Json {}' builder to convert such maps to [key1, value1, key2, value2,...] arrays."
+
+// special strings
+internal const val NULL = "null"
+
+// special chars
+internal const val COMMA = ','
+internal const val COLON = ':'
+internal const val BEGIN_OBJ = '{'
+internal const val END_OBJ = '}'
+internal const val BEGIN_LIST = '['
+internal const val END_LIST = ']'
+internal const val STRING = '"'
+internal const val STRING_ESC = '\\'
+
+internal const val INVALID = 0.toChar()
+internal const val UNICODE_ESC = 'u'
+
+// token classes
+internal const val TC_OTHER: Byte = 0
+internal const val TC_STRING: Byte = 1
+internal const val TC_STRING_ESC: Byte = 2
+internal const val TC_WHITESPACE: Byte = 3
+internal const val TC_COMMA: Byte = 4
+internal const val TC_COLON: Byte = 5
+internal const val TC_BEGIN_OBJ: Byte = 6
+internal const val TC_END_OBJ: Byte = 7
+internal const val TC_BEGIN_LIST: Byte = 8
+internal const val TC_END_LIST: Byte = 9
+internal const val TC_EOF: Byte = 10
+internal const val TC_INVALID: Byte = Byte.MAX_VALUE
+
+// mapping from chars to token classes
+private const val CTC_MAX = 0x7e
+
+// mapping from escape chars real chars
+private const val ESC2C_MAX = 0x75
+
+internal const val asciiCaseMask = 1 shl 5
+
+// object instead of @SharedImmutable because there is mutual initialization in [initC2ESC] and [initC2TC]
+internal object CharMappings {
+ @JvmField
+ val ESCAPE_2_CHAR = CharArray(ESC2C_MAX)
+
+ @JvmField
+ val CHAR_TO_TOKEN = ByteArray(CTC_MAX)
+
+ init {
+ initEscape()
+ initCharToToken()
+ }
+
+ private fun initEscape() {
+ for (i in 0x00..0x1f) {
+ initC2ESC(i, UNICODE_ESC)
+ }
+
+ initC2ESC(0x08, 'b')
+ initC2ESC(0x09, 't')
+ initC2ESC(0x0a, 'n')
+ initC2ESC(0x0c, 'f')
+ initC2ESC(0x0d, 'r')
+ initC2ESC('/', '/')
+ initC2ESC(STRING, STRING)
+ initC2ESC(STRING_ESC, STRING_ESC)
+ }
+
+ private fun initCharToToken() {
+ for (i in 0..0x20) {
+ initC2TC(i, TC_INVALID)
+ }
+
+ initC2TC(0x09, TC_WHITESPACE)
+ initC2TC(0x0a, TC_WHITESPACE)
+ initC2TC(0x0d, TC_WHITESPACE)
+ initC2TC(0x20, TC_WHITESPACE)
+ initC2TC(COMMA, TC_COMMA)
+ initC2TC(COLON, TC_COLON)
+ initC2TC(BEGIN_OBJ, TC_BEGIN_OBJ)
+ initC2TC(END_OBJ, TC_END_OBJ)
+ initC2TC(BEGIN_LIST, TC_BEGIN_LIST)
+ initC2TC(END_LIST, TC_END_LIST)
+ initC2TC(STRING, TC_STRING)
+ initC2TC(STRING_ESC, TC_STRING_ESC)
+ }
+
+ private fun initC2ESC(c: Int, esc: Char) {
+ if (esc != UNICODE_ESC) ESCAPE_2_CHAR[esc.code] = c.toChar()
+ }
+
+ private fun initC2ESC(c: Char, esc: Char) = initC2ESC(c.code, esc)
+
+ private fun initC2TC(c: Int, cl: Byte) {
+ CHAR_TO_TOKEN[c] = cl
+ }
+
+ private fun initC2TC(c: Char, cl: Byte) = initC2TC(c.code, cl)
+}
+
+internal fun charToTokenClass(c: Char) = if (c.code < CTC_MAX) CHAR_TO_TOKEN[c.code] else TC_OTHER
+
+internal fun escapeToChar(c: Int): Char = if (c < ESC2C_MAX) ESCAPE_2_CHAR[c] else INVALID
+
+/**
+ * The base class that reads the JSON from the given char sequence source.
+ * It has two implementations: one over the raw [String] instance, [StringJsonLexer],
+ * and one over an arbitrary stream of data, [ReaderJsonLexer] (JVM-only).
+ *
+ * [AbstractJsonLexer] contains base implementation for cold or not performance-sensitive
+ * methods on top of [CharSequence], but [StringJsonLexer] overrides some
+ * of them for the performance reasons (devirtualization of [CharSequence] and avoid
+ * of additional spills).
+ */
+internal abstract class AbstractJsonLexer {
+
+ protected abstract val source: CharSequence
+
+ @JvmField
+ protected var currentPosition: Int = 0 // position in source
+
+ @JvmField
+ val path = JsonPath()
+
+ open fun ensureHaveChars() {}
+
+ fun isNotEof(): Boolean = peekNextToken() != TC_EOF
+
+ // Used as bound check in loops
+ abstract fun prefetchOrEof(position: Int): Int
+
+ abstract fun tryConsumeComma(): Boolean
+
+ abstract fun canConsumeValue(): Boolean
+
+ abstract fun consumeNextToken(): Byte
+
+ protected fun isValidValueStart(c: Char): Boolean {
+ return when (c) {
+ '}', ']', ':', ',' -> false
+ else -> true
+ }
+ }
+
+ fun expectEof() {
+ val nextToken = consumeNextToken()
+ if (nextToken != TC_EOF)
+ fail("Expected EOF after parsing, but had ${source[currentPosition - 1]} instead")
+ }
+
+ /*
+ * Peeked string for coerced enums.
+ * If the value was picked, 'consumeString' will take it without scanning the source.
+ */
+ private var peekedString: String? = null
+ protected var escapedString = StringBuilder()
+
+ // TODO consider replacing usages of this method in JsonParser with char overload
+ fun consumeNextToken(expected: Byte): Byte {
+ val token = consumeNextToken()
+ if (token != expected) {
+ fail(expected)
+ }
+ return token
+ }
+
+ open fun consumeNextToken(expected: Char) {
+ ensureHaveChars()
+ val source = source
+ var cpos = currentPosition
+ while (true) {
+ cpos = prefetchOrEof(cpos)
+ if (cpos == -1) break // could be inline function but KT-1436
+ val c = source[cpos++]
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t') continue
+ currentPosition = cpos
+ if (c == expected) return
+ unexpectedToken(expected)
+ }
+ currentPosition = cpos
+ unexpectedToken(expected) // EOF
+ }
+
+ protected fun unexpectedToken(expected: Char) {
+ --currentPosition // To properly handle null
+ if (currentPosition >= 0 && expected == STRING && consumeStringLenient() == NULL) {
+ fail("Expected string literal but 'null' literal was found", currentPosition - 4, coerceInputValuesHint)
+ }
+ fail(charToTokenClass(expected))
+ }
+
+ internal fun fail(expectedToken: Byte): Nothing {
+ // We know that the token was consumed prior to this call
+ // Slow path, never called in normal code, can avoid optimizing it
+ val expected = when (expectedToken) {
+ TC_STRING -> "quotation mark '\"'"
+ TC_COMMA -> "comma ','"
+ TC_COLON -> "semicolon ':'"
+ TC_BEGIN_OBJ -> "start of the object '{'"
+ TC_END_OBJ -> "end of the object '}'"
+ TC_BEGIN_LIST -> "start of the array '['"
+ TC_END_LIST -> "end of the array ']'"
+ else -> "valid token" // should never happen
+ }
+ val s = if (currentPosition == source.length || currentPosition <= 0) "EOF" else source[currentPosition - 1].toString()
+ fail("Expected $expected, but had '$s' instead", currentPosition - 1)
+ }
+
+ fun peekNextToken(): Byte {
+ val source = source
+ var cpos = currentPosition
+ while (true) {
+ cpos = prefetchOrEof(cpos)
+ if (cpos == -1) break
+ val ch = source[cpos]
+ if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') {
+ ++cpos
+ continue
+ }
+ currentPosition = cpos
+ return charToTokenClass(ch)
+ }
+ currentPosition = cpos
+ return TC_EOF
+ }
+
+ /**
+ * Tries to consume `null` token from input.
+ * Returns `true` if the next 4 chars in input are not `null`,
+ * `false` otherwise and consumes it.
+ */
+ fun tryConsumeNotNull(): Boolean {
+ var current = skipWhitespaces()
+ current = prefetchOrEof(current)
+ // Cannot consume null due to EOF, maybe something else
+ val len = source.length - current
+ if (len < 4 || current == -1) return true
+ for (i in 0..3) {
+ if (NULL[i] != source[current + i]) return true
+ }
+ /*
+ * If we're in lenient mode, this might be the string with 'null' prefix,
+ * distinguish it from 'null'
+ */
+ if (len > 4 && charToTokenClass(source[current + 4]) == TC_OTHER) return true
+ currentPosition = current + 4
+ return false
+ }
+
+ open fun skipWhitespaces(): Int {
+ var current = currentPosition
+ // Skip whitespaces
+ while (true) {
+ current = prefetchOrEof(current)
+ if (current == -1) break
+ val c = source[current]
+ // Faster than char2TokenClass actually
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
+ ++current
+ } else {
+ break
+ }
+ }
+ currentPosition = current
+ return current
+ }
+
+ fun peekString(isLenient: Boolean): String? {
+ val token = peekNextToken()
+ val string = if (isLenient) {
+ if (token != TC_STRING && token != TC_OTHER) return null
+ consumeStringLenient()
+ } else {
+ if (token != TC_STRING) return null
+ consumeString()
+ }
+ peekedString = string
+ return string
+ }
+
+ open fun indexOf(char: Char, startPos: Int) = source.indexOf(char, startPos)
+ open fun substring(startPos: Int, endPos: Int) = source.substring(startPos, endPos)
+
+ /*
+ * This method is a copy of consumeString, but used for key of json objects, so there
+ * is no need to lookup peeked string.
+ */
+ abstract fun consumeKeyString(): String
+
+ fun consumeString(): String {
+ if (peekedString != null) {
+ return takePeeked()
+ }
+
+ return consumeKeyString()
+ }
+
+ @JsName("consumeString2") // WA for JS issue
+ protected fun consumeString(source: CharSequence, startPosition: Int, current: Int): String {
+ var currentPosition = current
+ var lastPosition = startPosition
+ var char = source[currentPosition] // Avoid two range checks visible in the profiler
+ var usedAppend = false
+ while (char != STRING) {
+ if (char == STRING_ESC) {
+ usedAppend = true
+ currentPosition = prefetchOrEof(appendEscape(lastPosition, currentPosition))
+ if (currentPosition == -1)
+ fail("EOF", currentPosition)
+ lastPosition = currentPosition
+ } else if (++currentPosition >= source.length) {
+ usedAppend = true
+ // end of chunk
+ appendRange(lastPosition, currentPosition)
+ currentPosition = prefetchOrEof(currentPosition)
+ if (currentPosition == -1)
+ fail("EOF", currentPosition)
+ lastPosition = currentPosition
+ }
+ char = source[currentPosition]
+ }
+
+ val string = if (!usedAppend) {
+ // there was no escaped chars
+ substring(lastPosition, currentPosition)
+ } else {
+ // some escaped chars were there
+ decodedString(lastPosition, currentPosition)
+ }
+ this.currentPosition = currentPosition + 1
+ return string
+ }
+
+ private fun appendEscape(lastPosition: Int, current: Int): Int {
+ appendRange(lastPosition, current)
+ return appendEsc(current + 1)
+ }
+
+ private fun decodedString(lastPosition: Int, currentPosition: Int): String {
+ appendRange(lastPosition, currentPosition)
+ val result = escapedString.toString()
+ escapedString.setLength(0)
+ return result
+ }
+
+ private fun takePeeked(): String {
+ return peekedString!!.also { peekedString = null }
+ }
+
+ fun consumeStringLenientNotNull(): String {
+ val result = consumeStringLenient()
+ /*
+ * Check if lenient value is 'null' _without_ quotation marks and fail for non-nullable read if so.
+ */
+ if (result == NULL && wasUnquotedString()) {
+ fail("Unexpected 'null' value instead of string literal")
+ }
+ return result
+ }
+
+ private fun wasUnquotedString(): Boolean {
+ // Is invoked _only_ when the 'null' string was read, thus 'cP - 1' is always within bounds
+ return source[currentPosition - 1] != STRING
+ }
+
+ // Allows consuming unquoted string
+ fun consumeStringLenient(): String {
+ if (peekedString != null) {
+ return takePeeked()
+ }
+ var current = skipWhitespaces()
+ if (current >= source.length || current == -1) fail("EOF", current)
+ val token = charToTokenClass(source[current])
+ if (token == TC_STRING) {
+ return consumeString()
+ }
+
+ if (token != TC_OTHER) {
+ fail("Expected beginning of the string, but got ${source[current]}")
+ }
+ var usedAppend = false
+ while (charToTokenClass(source[current]) == TC_OTHER) {
+ ++current
+ if (current >= source.length) {
+ usedAppend = true
+ appendRange(currentPosition, current)
+ val eof = prefetchOrEof(current)
+ if (eof == -1) {
+ // to handle plain lenient strings, such as top-level
+ currentPosition = current
+ return decodedString(0, 0)
+ } else {
+ current = eof
+ }
+ }
+ }
+ val result = if (!usedAppend) {
+ substring(currentPosition, current)
+ } else {
+ decodedString(currentPosition, current)
+ }
+ currentPosition = current
+ return result
+ }
+
+ // initializes buf usage upon the first encountered escaped char
+ protected open fun appendRange(fromIndex: Int, toIndex: Int) {
+ escapedString.append(source, fromIndex, toIndex)
+ }
+
+ private fun appendEsc(startPosition: Int): Int {
+ var currentPosition = startPosition
+ currentPosition = prefetchOrEof(currentPosition)
+ if (currentPosition == -1) fail("Expected escape sequence to continue, got EOF")
+ val currentChar = source[currentPosition++]
+ if (currentChar == UNICODE_ESC) {
+ return appendHex(source, currentPosition)
+ }
+
+ val c = escapeToChar(currentChar.code)
+ if (c == INVALID) fail("Invalid escaped char '$currentChar'")
+ escapedString.append(c)
+ return currentPosition
+ }
+
+ private fun appendHex(source: CharSequence, startPos: Int): Int {
+ if (startPos + 4 >= source.length) {
+ currentPosition = startPos
+ ensureHaveChars()
+ if (currentPosition + 4 >= source.length)
+ fail("Unexpected EOF during unicode escape")
+ return appendHex(source, currentPosition)
+ }
+ escapedString.append(
+ ((fromHexChar(source, startPos) shl 12) +
+ (fromHexChar(source, startPos + 1) shl 8) +
+ (fromHexChar(source, startPos + 2) shl 4) +
+ fromHexChar(source, startPos + 3)).toChar()
+ )
+ return startPos + 4
+ }
+
+ internal inline fun require(condition: Boolean, position: Int = currentPosition, message: () -> String) {
+ if (!condition) fail(message(), position)
+ }
+
+ private fun fromHexChar(source: CharSequence, currentPosition: Int): Int {
+ return when (val character = source[currentPosition]) {
+ in '0'..'9' -> character.code - '0'.code
+ in 'a'..'f' -> character.code - 'a'.code + 10
+ in 'A'..'F' -> character.code - 'A'.code + 10
+ else -> fail("Invalid toHexChar char '$character' in unicode escape")
+ }
+ }
+
+ fun skipElement(allowLenientStrings: Boolean) {
+ val tokenStack = mutableListOf<Byte>()
+ var lastToken = peekNextToken()
+ if (lastToken != TC_BEGIN_LIST && lastToken != TC_BEGIN_OBJ) {
+ consumeStringLenient()
+ return
+ }
+ while (true) {
+ lastToken = peekNextToken()
+ if (lastToken == TC_STRING) {
+ if (allowLenientStrings) consumeStringLenient() else consumeKeyString()
+ continue
+ }
+ when (lastToken) {
+ TC_BEGIN_LIST, TC_BEGIN_OBJ -> {
+ tokenStack.add(lastToken)
+ }
+ TC_END_LIST -> {
+ if (tokenStack.last() != TC_BEGIN_LIST) throw JsonDecodingException(
+ currentPosition,
+ "found ] instead of } at path: $path",
+ source
+ )
+ tokenStack.removeLast()
+ }
+ TC_END_OBJ -> {
+ if (tokenStack.last() != TC_BEGIN_OBJ) throw JsonDecodingException(
+ currentPosition,
+ "found } instead of ] at path: $path",
+ source
+ )
+ tokenStack.removeLast()
+ }
+ TC_EOF -> fail("Unexpected end of input due to malformed JSON during ignoring unknown keys")
+ }
+ consumeNextToken()
+ if (tokenStack.size == 0) return
+ }
+ }
+
+ override fun toString(): String {
+ return "JsonReader(source='$source', currentPosition=$currentPosition)"
+ }
+
+ fun failOnUnknownKey(key: String) {
+ // At this moment we already have both key and semicolon (and whitespaces! consumed),
+ // but still would like an error to point to the beginning of the key, so we are backtracking it
+ val processed = substring(0, currentPosition)
+ val lastIndexOf = processed.lastIndexOf(key)
+ fail("Encountered an unknown key '$key'", lastIndexOf, ignoreUnknownKeysHint)
+ }
+
+ fun fail(message: String, position: Int = currentPosition, hint: String = ""): Nothing {
+ val hintMessage = if (hint.isEmpty()) "" else "\n$hint"
+ throw JsonDecodingException(position, message + " at path: " + path.getPath() + hintMessage, source)
+ }
+
+ fun consumeNumericLiteral(): Long {
+ /*
+ * This is an optimized (~40% for numbers) version of consumeString().toLong()
+ * that doesn't allocate and also doesn't support any radix but 10
+ */
+ var current = skipWhitespaces()
+ current = prefetchOrEof(current)
+ if (current >= source.length || current == -1) fail("EOF")
+ val hasQuotation = if (source[current] == STRING) {
+ // Check it again
+ // not sure if should call ensureHaveChars() because threshold is far greater than chars count in MAX_LONG
+ if (++current == source.length) fail("EOF")
+ true
+ } else {
+ false
+ }
+ var accumulator = 0L
+ var isNegative = false
+ val start = current
+ var hasChars = true
+ while (hasChars) {
+ val ch: Char = source[current]
+ if (ch == '-') {
+ if (current != start) fail("Unexpected symbol '-' in numeric literal")
+ isNegative = true
+ ++current
+ continue
+ }
+ val token = charToTokenClass(ch)
+ if (token != TC_OTHER) break
+ ++current
+ hasChars = current != source.length
+ val digit = ch - '0'
+ if (digit !in 0..9) fail("Unexpected symbol '$ch' in numeric literal")
+ accumulator = accumulator * 10 - digit
+ if (accumulator > 0) fail("Numeric value overflow")
+ }
+ if (start == current || (isNegative && start == current - 1)) {
+ fail("Expected numeric literal")
+ }
+ if (hasQuotation) {
+ if (!hasChars) fail("EOF")
+ if (source[current] != STRING) fail("Expected closing quotation mark")
+ ++current
+ }
+ currentPosition = current
+ return when {
+ isNegative -> accumulator
+ accumulator != Long.MIN_VALUE -> -accumulator
+ else -> fail("Numeric value overflow")
+ }
+ }
+
+
+ fun consumeBoolean(): Boolean {
+ return consumeBoolean(skipWhitespaces())
+ }
+
+ fun consumeBooleanLenient(): Boolean {
+ var current = skipWhitespaces()
+ if (current == source.length) fail("EOF")
+ val hasQuotation = if (source[current] == STRING) {
+ ++current
+ true
+ } else {
+ false
+ }
+ val result = consumeBoolean(current)
+ if (hasQuotation) {
+ if (currentPosition == source.length) fail("EOF")
+ if (source[currentPosition] != STRING)
+ fail("Expected closing quotation mark")
+ ++currentPosition
+ }
+ return result
+ }
+
+ @JsName("consumeBoolean2") // WA for JS issue
+ private fun consumeBoolean(start: Int): Boolean {
+ /*
+ * In ASCII representation, upper and lower case letters are different
+ * in 6-th bit and we leverage this fact, our implementation consumes boolean literals
+ * in a case-insensitive manner.
+ */
+ var current = prefetchOrEof(start)
+ if (current >= source.length || current == -1) fail("EOF")
+ return when (source[current++].code or asciiCaseMask) {
+ 't'.code -> {
+ consumeBooleanLiteral("rue", current)
+ true
+ }
+ 'f'.code -> {
+ consumeBooleanLiteral("alse", current)
+ false
+ }
+ else -> {
+ fail("Expected valid boolean literal prefix, but had '${consumeStringLenient()}'")
+ }
+ }
+ }
+
+ private fun consumeBooleanLiteral(literalSuffix: String, current: Int) {
+ if (source.length - current < literalSuffix.length) {
+ fail("Unexpected end of boolean literal")
+ }
+
+ for (i in literalSuffix.indices) {
+ val expected = literalSuffix[i]
+ val actual = source[current + i]
+ if (expected.code != actual.code or asciiCaseMask) {
+ fail("Expected valid boolean literal prefix, but had '${consumeStringLenient()}'")
+ }
+ }
+
+ currentPosition = current + literalSuffix.length
+ }
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt
new file mode 100644
index 00000000..0ff980a2
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+internal class StringJsonLexer(override val source: String) : AbstractJsonLexer() {
+
+ override fun prefetchOrEof(position: Int): Int = if (position < source.length) position else -1
+
+ override fun consumeNextToken(): Byte {
+ val source = source
+ while (currentPosition != -1 && currentPosition < source.length) {
+ val ch = source[currentPosition++]
+ return when (val tc = charToTokenClass(ch)) {
+ TC_WHITESPACE -> continue
+ else -> tc
+ }
+ }
+ return TC_EOF
+ }
+
+ override fun tryConsumeComma(): Boolean {
+ val current = skipWhitespaces()
+ if (current == source.length || current == -1) return false
+ if (source[current] == ',') {
+ ++currentPosition
+ return true
+ }
+ return false
+ }
+
+ override fun canConsumeValue(): Boolean {
+ var current = currentPosition
+ if (current == -1) return false
+ while (current < source.length) {
+ val c = source[current]
+ // Inlined skipWhitespaces without field spill and nested loop. Also faster then char2TokenClass
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
+ ++current
+ continue
+ }
+ currentPosition = current
+ return isValidValueStart(c)
+ }
+ currentPosition = current
+ return false
+ }
+
+ override fun skipWhitespaces(): Int {
+ var current = currentPosition
+ if (current == -1) return current
+ // Skip whitespaces
+ while (current < source.length) {
+ val c = source[current]
+ // Faster than char2TokenClass actually
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
+ ++current
+ } else {
+ break
+ }
+ }
+ currentPosition = current
+ return current
+ }
+
+ override fun consumeNextToken(expected: Char) {
+ if (currentPosition == -1) unexpectedToken(expected)
+ val source = source
+ while (currentPosition < source.length) {
+ val c = source[currentPosition++]
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t') continue
+ if (c == expected) return
+ unexpectedToken(expected)
+ }
+ unexpectedToken(expected) // EOF
+ }
+
+ override fun consumeKeyString(): String {
+ /*
+ * For strings we assume that escaped symbols are rather an exception, so firstly
+ * we optimistically scan for closing quote via intrinsified and blazing-fast 'indexOf',
+ * than do our pessimistic check for backslash and fallback to slow-path if necessary.
+ */
+ consumeNextToken(STRING)
+ val current = currentPosition
+ val closingQuote = source.indexOf('"', current)
+ if (closingQuote == -1) fail(TC_STRING)
+ // Now we _optimistically_ know where the string ends (it might have been an escaped quote)
+ for (i in current until closingQuote) {
+ // Encountered escape sequence, should fallback to "slow" path and symbolic scanning
+ if (source[i] == STRING_ESC) {
+ return consumeString(source, currentPosition, i)
+ }
+ }
+ this.currentPosition = closingQuote + 1
+ return source.substring(current, closingQuote)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/ClassWithMultipleMasksTest.kt b/formats/json/commonTest/src/kotlinx/serialization/ClassWithMultipleMasksTest.kt
new file mode 100644
index 00000000..cc0158c1
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/ClassWithMultipleMasksTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.json.Json
+import kotlin.test.*
+
+class ClassWithMultipleMasksTest {
+
+ /*
+ * Plugin generates int mask for each 32 fields.
+ * This test ensures that mask is properly generated when fields count is greater than 32.
+ */
+ @Serializable
+ data class BigDummyData(
+ val regular: String,
+ @SerialName("field0") val field0: String? = null,
+ @SerialName("field1") val field1: String? = null,
+ @SerialName("field2") val field2: String? = null,
+ @SerialName("field3") val field3: String? = null,
+ @SerialName("field4") val field4: String? = null,
+ @SerialName("field5") val field5: String? = null,
+ @SerialName("field6") val field6: String? = null,
+ @SerialName("field7") val field7: String? = null,
+ @SerialName("field8") val field8: String? = null,
+ @SerialName("field9") val field9: String? = null,
+ @SerialName("field10") val field10: String? = null,
+ @SerialName("field11") val field11: String? = null,
+ @SerialName("field12") val field12: String? = null,
+ @SerialName("field13") val field13: String? = null,
+ @SerialName("field14") val field14: String? = null,
+ @SerialName("field15") val field15: String? = null,
+ @SerialName("field16") val field16: String? = null,
+ @SerialName("field17") val field17: String? = null,
+ @SerialName("field18") val field18: String? = null,
+ @SerialName("field19") val field19: String? = null,
+ @SerialName("field20") val field20: String? = null,
+ @SerialName("field21") val field21: String? = null,
+ @SerialName("field22") val field22: String? = null,
+ @SerialName("field23") val field23: String? = null,
+ @SerialName("field24") val field24: String? = null,
+ @SerialName("field25") val field25: String? = null,
+ @SerialName("field26") val field26: String? = null,
+ @SerialName("field27") val field27: String? = null,
+ @SerialName("field28") val field28: String? = null,
+ @SerialName("field29") val field29: String? = null,
+ @SerialName("field30") val field30: String? = null,
+ @SerialName("field31") val field31: String? = null,
+ @SerialName("field32") val field32: String? = null,
+ @SerialName("field33") val field33: String? = null,
+ @SerialName("field34") val field34: String? = null,
+ @SerialName("field35") val field35: String? = null,
+ @SerialName("field36") val field36: String? = null,
+ @SerialName("field37") val field37: String? = null,
+ @SerialName("field38") val field38: String? = null,
+ @SerialName("field39") val field39: String? = null,
+ @SerialName("field40") val field40: String? = "b",
+ @Required val requiredLast: String = "required"
+ )
+
+ @Test
+ fun testMoreThan32Fields() {
+ val data = BigDummyData("a")
+ val message = Json.encodeToString(BigDummyData.serializer(), data)
+ println(message)
+ val restored = Json.decodeFromString(BigDummyData.serializer(), """{"regular": "0","requiredLast":"r"}""")
+ with(restored) {
+ assertEquals("0", regular)
+ assertEquals("b", field40)
+ assertEquals(null, field39)
+ assertEquals("r", requiredLast)
+ }
+
+ val restored2 = Json.decodeFromString(BigDummyData.serializer(), """{"regular": "0", "field39":"f","requiredLast":"required"}""")
+ with(restored2) {
+ assertEquals("0", regular)
+ assertEquals("b", field40)
+ assertEquals("f", field39)
+ assertEquals("required", requiredLast)
+ }
+ assertFailsWith<SerializationException> { Json.decodeFromString(BigDummyData.serializer(), """{"regular": "0"}""") }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/EncodingCollectionsTest.kt b/formats/json/commonTest/src/kotlinx/serialization/EncodingCollectionsTest.kt
new file mode 100644
index 00000000..cd077e04
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/EncodingCollectionsTest.kt
@@ -0,0 +1,27 @@
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.ListSerializer
+import kotlinx.serialization.builtins.serializer
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+class EncodingCollectionsTest {
+ object ListSerializer : KSerializer<List<String>> {
+ override val descriptor: SerialDescriptor = ListSerializer(String.serializer()).descriptor
+
+ override fun serialize(encoder: Encoder, value: List<String>) {
+ encoder.encodeCollection(descriptor, value) { index, item ->
+ encodeStringElement(descriptor, index, item)
+ }
+ }
+
+ override fun deserialize(decoder: Decoder): List<String> = throw NotImplementedError()
+ }
+
+ @Test
+ fun testEncoding() {
+ assertEquals("""["Hello","World!"]""", Json.encodeToString(ListSerializer, listOf("Hello", "World!")))
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/EncodingExtensionsTest.kt b/formats/json/commonTest/src/kotlinx/serialization/EncodingExtensionsTest.kt
new file mode 100644
index 00000000..e8a0c487
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/EncodingExtensionsTest.kt
@@ -0,0 +1,41 @@
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+class EncodingExtensionsTest {
+
+ @Serializable(with = BoxSerializer::class)
+ class Box(val i: Int)
+
+ @Serializer(forClass = Box::class)
+ object BoxSerializer : KSerializer<Box> {
+ override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Box") {
+ element<Int>("i")
+ }
+
+ override fun serialize(encoder: Encoder, value: Box) {
+ encoder.encodeStructure(descriptor) {
+ throw ArithmeticException()
+ }
+ }
+
+ override fun deserialize(decoder: Decoder): Box {
+ decoder.decodeStructure(descriptor) {
+ throw ArithmeticException()
+ }
+ }
+ }
+
+ @Test
+ fun testEncodingExceptionNotSwallowed() {
+ assertFailsWith<ArithmeticException> { Json.encodeToString(Box(1)) }
+ }
+
+ @Test
+ fun testDecodingExceptionNotSwallowed() {
+ assertFailsWith<ArithmeticException> { Json.decodeFromString<Box>("""{"i":1}""") }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/EnumSerializationTest.kt b/formats/json/commonTest/src/kotlinx/serialization/EnumSerializationTest.kt
new file mode 100644
index 00000000..c7350cee
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/EnumSerializationTest.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.JsonTestBase
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class EnumSerializationTest : JsonTestBase() {
+
+ @Serializable
+ enum class RegularEnum {
+ VALUE
+ }
+
+ @Serializable
+ data class Regular(val a: RegularEnum)
+
+ @Serializable
+ data class RegularNullable(val a: RegularEnum?)
+
+ @Serializable
+ @SerialName("custom_enum")
+ private enum class CustomEnum {
+ @SerialName("foo_a")
+ FooA,
+
+ @SerialName("foo_b")
+ @Id(10)
+ FooB
+ }
+
+ @Serializable
+ private data class WithCustomEnum(val c: CustomEnum)
+
+ @Serializable(CustomEnumSerializer::class)
+ private enum class WithCustom {
+ @SerialName("1")
+ ONE,
+ @SerialName("2")
+ TWO
+ }
+
+ @Serializer(WithCustom::class)
+ private class CustomEnumSerializer : KSerializer<WithCustom> {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("WithCustom", SerialKind.ENUM) {
+ element("1", buildSerialDescriptor("WithCustom.1", StructureKind.OBJECT))
+ element("2", buildSerialDescriptor("WithCustom.2", StructureKind.OBJECT))
+ }
+
+ override fun serialize(encoder: Encoder, value: WithCustom) {
+ encoder.encodeInt(value.ordinal + 1)
+ }
+
+ override fun deserialize(decoder: Decoder): WithCustom {
+ return WithCustom.values()[decoder.decodeInt() - 1]
+ }
+ }
+
+ @Serializable
+ private data class CustomInside(val inside: WithCustom)
+
+ @Test
+ fun testEnumSerialization() =
+ assertJsonFormAndRestored(
+ WithCustomEnum.serializer(),
+ WithCustomEnum(CustomEnum.FooB),
+ """{"c":"foo_b"}""",
+ default
+ )
+
+ @Test
+ fun testEnumWithCustomSerializers() =
+ assertJsonFormAndRestored(
+ CustomInside.serializer(),
+ CustomInside(WithCustom.TWO), """{"inside":2}"""
+ )
+
+
+ @Test
+ fun testHasMeaningfulToString() {
+ val regular = Regular.serializer().descriptor.toString()
+ assertEquals(
+ "kotlinx.serialization.EnumSerializationTest.Regular(a: kotlinx.serialization.EnumSerializationTest.RegularEnum)",
+ regular
+ )
+ val regularNullable = RegularNullable.serializer().descriptor.toString()
+ assertEquals(
+ "kotlinx.serialization.EnumSerializationTest.RegularNullable(a: kotlinx.serialization.EnumSerializationTest.RegularEnum?)",
+ regularNullable
+ )
+ // slightly differs from previous one
+ val regularNullableJoined = RegularNullable.serializer().descriptor.elementDescriptors.joinToString()
+ assertEquals("kotlinx.serialization.EnumSerializationTest.RegularEnum(VALUE)?", regularNullableJoined)
+
+ val regularEnum = RegularEnum.serializer().descriptor.toString()
+ assertEquals("kotlinx.serialization.EnumSerializationTest.RegularEnum(VALUE)", regularEnum)
+ }
+
+
+ @Test
+ fun testHasMeaningfulHashCode() {
+ val a = Regular.serializer().descriptor.hashCode()
+ val b = RegularNullable.serializer().descriptor.hashCode()
+ val c = RegularEnum.serializer().descriptor.hashCode()
+ assertTrue(setOf(a, b, c).size == 3, ".hashCode must give different result for different descriptors")
+ }
+
+ enum class MyEnum {
+ A, B, C;
+ }
+
+ @Serializable
+ @SerialName("kotlinx.serialization.EnumSerializationTest.MyEnum")
+ enum class MyEnum2 {
+ A, B, C;
+ }
+
+ @Serializable
+ class Wrapper(val a: MyEnum)
+
+ @Test
+ fun testStructurallyEqualDescriptors() {
+ val libraryGenerated = Wrapper.serializer().descriptor.getElementDescriptor(0)
+ val codeGenerated = MyEnum2.serializer().descriptor
+ assertNotEquals(libraryGenerated::class, codeGenerated::class)
+ libraryGenerated.assertDescriptorEqualsTo(codeGenerated)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/GenericSerializersOnFileTest.kt b/formats/json/commonTest/src/kotlinx/serialization/GenericSerializersOnFileTest.kt
new file mode 100644
index 00000000..c3003ca9
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/GenericSerializersOnFileTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:UseSerializers(GenericSerializersOnFileTest.MySerializer::class)
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.PrimitiveKind
+import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.json.Json
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class GenericSerializersOnFileTest {
+ data class GenericClass<T>(val t: T)
+
+ @Serializable
+ data class Holder(val notnull: GenericClass<String>, val nullable: GenericClass<String>?)
+
+ class MySerializer<E>(val tSer: KSerializer<E>) : KSerializer<GenericClass<E>> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("my int descriptor", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: GenericClass<E>) {
+ encoder.encodeString(value.t as String)
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ override fun deserialize(decoder: Decoder): GenericClass<E> {
+ return GenericClass(decoder.decodeString() as E)
+ }
+ }
+
+ @Test
+ fun testSerialize() {
+ assertEquals(
+ """{"notnull":"Not Null","nullable":null}""",
+ Json.encodeToString(Holder(notnull = GenericClass("Not Null"), nullable = null))
+ )
+ assertEquals(
+ """{"notnull":"Not Null","nullable":"Nullable"}""",
+ Json.encodeToString(Holder(notnull = GenericClass("Not Null"), nullable = GenericClass("Nullable")))
+ )
+ }
+
+ @Test
+ fun testDeserialize() {
+ assertEquals(
+ Holder(notnull = GenericClass("Not Null"), nullable = null),
+ Json.decodeFromString("""{"notnull":"Not Null","nullable":null}""")
+ )
+ assertEquals(
+ Holder(notnull = GenericClass("Not Null"), nullable = GenericClass("Nullable")),
+ Json.decodeFromString("""{"notnull":"Not Null","nullable":"Nullable"}""")
+ )
+ }
+
+
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/JsonOverwriteKeyTest.kt b/formats/json/commonTest/src/kotlinx/serialization/JsonOverwriteKeyTest.kt
new file mode 100644
index 00000000..b2425a1f
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/JsonOverwriteKeyTest.kt
@@ -0,0 +1,36 @@
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+class JsonOverwriteKeyTest : JsonTestBase() {
+ private val json = Json
+
+ @Serializable
+ data class Data(val a: Int)
+
+ @Serializable
+ data class Updatable(val d: Data)
+
+ @Test
+ fun testLatestValueWins() {
+ val parsed: Updatable = default.decodeFromString("""{"d":{"a":"42"},"d":{"a":43}}""")
+ assertEquals(Data(43), parsed.d)
+ }
+
+ @Serializable
+ data class WrappedMap<T>(val mp: Map<String, T>)
+
+ @Test
+ fun testLatestKeyInMap() {
+ val parsed = json.decodeFromString(WrappedMap.serializer(Int.serializer()), """{"mp": { "x" : 23, "x" : 42, "y": 4 }}""")
+ assertEquals(WrappedMap(mapOf("x" to 42, "y" to 4)), parsed)
+ }
+
+ @Test
+ fun testLastestListValueInMap() {
+ val parsed = json.decodeFromString(WrappedMap.serializer(ListSerializer(Int.serializer())), """{"mp": { "x" : [23], "x" : [42], "y": [4] }}""")
+ assertEquals(WrappedMap(mapOf("x" to listOf(42), "y" to listOf(4))), parsed)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/JsonPathTest.kt b/formats/json/commonTest/src/kotlinx/serialization/JsonPathTest.kt
new file mode 100644
index 00000000..8d31ba22
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/JsonPathTest.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.json.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class JsonPathTest : JsonTestBase() {
+
+ @Serializable
+ class Outer(val a: Int, val i: Inner)
+
+ @Serializable
+ class Inner(val a: Int, val b: String, val c: List<String>, val d: Map<Int, Box>)
+
+ @Serializable
+ class Box(val s: String)
+
+ @Test
+ fun testBasicError() {
+ expectPath("$.a") { Json.decodeFromString<Outer>("""{"a":foo}""") }
+ expectPath("$.i") { Json.decodeFromString<Outer>("""{"a":42, "i":[]}""") }
+ expectPath("$.i.b") { Json.decodeFromString<Outer>("""{"a":42, "i":{"a":43, "b":42}""") }
+ expectPath("$.i.b") { Json.decodeFromString<Outer>("""{"a":42, "i":{"b":42}""") }
+ }
+
+ @Test
+ fun testMissingKey() {
+ expectPath("$.i.d['1']") { Json.decodeFromString<Outer>("""{"a":42, "i":{"d":{1:{}}""") }
+ }
+
+ @Test
+ fun testUnknownKeyIsProperlyReported() {
+ expectPath("$.i") { Json.decodeFromString<Outer>("""{"a":42, "i":{"foo":42}""") }
+ expectPath("$") { Json.decodeFromString<Outer>("""{"x":{}, "a": 42}""") }
+ // The only place we have misattribution in
+ // Json.decodeFromString<Outer>("""{"a":42, "x":{}}""")
+ }
+
+ @Test
+ fun testMalformedRootObject() {
+ expectPath("$") { Json.decodeFromString<Outer>("""{{""") }
+ }
+
+ @Test
+ fun testArrayIndex() {
+ expectPath("$.i.c[1]") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "c": ["a", 2]}""") }
+ expectPath("$[2]") { Json.decodeFromString<List<String>>("""["a", "2", 3]""") }
+ }
+
+ @Test
+ fun testArrayIndexMalformedArray() {
+ // Also zeroes as we cannot distinguish what exactly wen wrong is such cases
+ expectPath("$.i.c[0]") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "c": [[""") }
+ expectPath("$[0]") { Json.decodeFromString<List<String>>("""[[""") }
+ // But we can here
+ expectPath("$.i.c\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "c": {}}}""") }
+ expectPath("$\n") { Json.decodeFromString<List<String>>("""{""") }
+ }
+
+ @Test
+ fun testMapKey() {
+ expectPath("$.i.d\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {"foo": {}}""") }
+ expectPath("$.i.d\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {42: {"s":"s"}, 42.0:{}}""") }
+ expectPath("$\n") { Json.decodeFromString<Map<Int, String>>("""{"foo":"bar"}""") }
+ expectPath("$\n") { Json.decodeFromString<Map<Int, String>>("""{42:"bar", "foo":"bar"}""") }
+ expectPath("$['42']['foo']") { Json.decodeFromString<Map<Int, Map<String, Int>>>("""{42: {"foo":"bar"}""") }
+ }
+
+ @Test
+ fun testMalformedMap() {
+ expectPath("$.i.d\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": []""") }
+ expectPath("$\n") { Json.decodeFromString<Map<Int, String>>("""[]""") }
+ }
+
+ @Test
+ fun testMapValue() {
+ expectPath("$.i.d['42']\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {42: {"xx":"bar"}}""") }
+ expectPath("$.i.d['43']\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {42: {"s":"s"}, 43: {"xx":"bar"}}}""") }
+ expectPath("$['239']") { Json.decodeFromString<Map<Int, String>>("""{239:bar}""") }
+ }
+
+ @Serializable
+ class Fp(val d: Double)
+
+ @Test
+ fun testInvalidFp() {
+ expectPath("$.d") { Json.decodeFromString<Fp>("""{"d": NaN}""") }
+ }
+
+ @Serializable
+ class EH(val e: E)
+ enum class E
+
+ @Test
+ fun testUnknownEnum() {
+ expectPath("$.e") { Json.decodeFromString<EH>("""{"e": "foo"}""") }
+ }
+
+ @Serializable
+ @SerialName("f")
+ sealed class Sealed {
+
+ @Serializable
+ @SerialName("n")
+ class Nesting(val f: Sealed) : Sealed()
+
+ @Serializable
+ @SerialName("b")
+ class Box(val s: String) : Sealed()
+
+ @Serializable
+ @SerialName("d")
+ class DoubleNesting(val f: Sealed, val f2: Sealed) : Sealed()
+ }
+
+ // TODO use non-array polymorphism when https://github.com/Kotlin/kotlinx.serialization/issues/1839 is fixed
+ @Test
+ fun testHugeNestingToCheckResize() = jvmOnly {
+ val json = Json { useArrayPolymorphism = true }
+ var outer = Sealed.Nesting(Sealed.Box("value"))
+ repeat(100) {
+ outer = Sealed.Nesting(outer)
+ }
+ val str = json.encodeToString(Sealed.serializer(), outer)
+ // throw-away data
+ json.decodeFromString(Sealed.serializer(), str)
+
+ val malformed = str.replace("\"value\"", "42")
+ val expectedPath = "$" + ".value.f".repeat(101) + ".value.s"
+ expectPath(expectedPath) { json.decodeFromString(Sealed.serializer(), malformed) }
+ }
+
+ @Test
+ fun testDoubleNesting() = jvmOnly {
+ val json = Json { useArrayPolymorphism = true }
+ var outer1 = Sealed.Nesting(Sealed.Box("correct"))
+ repeat(64) {
+ outer1 = Sealed.Nesting(outer1)
+ }
+
+ var outer2 = Sealed.Nesting(Sealed.Box("incorrect"))
+ repeat(33) {
+ outer2 = Sealed.Nesting(outer2)
+ }
+
+ val str = json.encodeToString(Sealed.serializer(), Sealed.DoubleNesting(outer1, outer2))
+ // throw-away data
+ json.decodeFromString(Sealed.serializer(), str)
+
+ val malformed = str.replace("\"incorrect\"", "42")
+ val expectedPath = "$.value.f2" + ".value.f".repeat(34) + ".value.s"
+ expectPath(expectedPath) { json.decodeFromString(Sealed.serializer(), malformed) }
+ }
+
+ private inline fun expectPath(path: String, block: () -> Unit) {
+ val message = runCatching { block() }
+ .exceptionOrNull()!!.message!!
+ assertContains(message, path)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/NotNullSerializersCompatibilityOnFileTest.kt b/formats/json/commonTest/src/kotlinx/serialization/NotNullSerializersCompatibilityOnFileTest.kt
new file mode 100644
index 00000000..b44a37f3
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/NotNullSerializersCompatibilityOnFileTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:UseSerializers(NotNullSerializersCompatibilityOnFileTest.NonNullableIntSerializer::class)
+@file:UseContextualSerialization(NotNullSerializersCompatibilityOnFileTest.FileContextualType::class)
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.SerializersModule
+import kotlinx.serialization.modules.contextual
+import kotlin.test.*
+
+class NotNullSerializersCompatibilityOnFileTest {
+ data class FileContextualType(val text: String)
+
+ object FileContextualSerializer : KSerializer<FileContextualType> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("FileContextualSerializer", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: FileContextualType) {
+ return encoder.encodeString(value.text)
+ }
+
+ override fun deserialize(decoder: Decoder): FileContextualType {
+ return FileContextualType(decoder.decodeString())
+ }
+ }
+
+ @Serializable
+ data class FileContextualHolder(val nullable: FileContextualType?, val nonNullable: FileContextualType)
+
+
+ data class ContextualType(val text: String)
+
+ object ContextualSerializer : KSerializer<ContextualType> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("FileContextualSerializer", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: ContextualType) {
+ return encoder.encodeString(value.text)
+ }
+
+ override fun deserialize(decoder: Decoder): ContextualType {
+ return ContextualType(decoder.decodeString())
+ }
+ }
+
+ @Serializable
+ data class ContextualHolder(@Contextual val nullable: ContextualType?, @Contextual val nonNullable: ContextualType)
+
+
+ @Serializable
+ data class Holder(val nullable: Int?, val nonNullable: Int)
+
+ @Serializer(forClass = Int::class)
+ object NonNullableIntSerializer : KSerializer<Int> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("NotNullIntSerializer", PrimitiveKind.INT)
+
+ override fun serialize(encoder: Encoder, value: Int) {
+ return encoder.encodeInt(value + 2)
+ }
+
+ override fun deserialize(decoder: Decoder): Int {
+ return (decoder.decodeInt() - 2)
+ }
+ }
+
+ @Test
+ fun testFileLevel() {
+ assertEquals("""{"nullable":null,"nonNullable":52}""", Json.encodeToString(Holder(nullable = null, nonNullable = 50)))
+ assertEquals("""{"nullable":2,"nonNullable":2}""", Json.encodeToString(Holder(nullable = 0, nonNullable = 0)))
+ assertEquals("""{"nullable":12,"nonNullable":52}""", Json.encodeToString(Holder(nullable = 10, nonNullable = 50)))
+
+ assertEquals(Holder(nullable = 0, nonNullable = 50), Json.decodeFromString("""{"nullable":2,"nonNullable":52}"""))
+ assertEquals(Holder(nullable = null, nonNullable = 50), Json.decodeFromString("""{"nullable":null,"nonNullable":52}"""))
+ assertEquals(Holder(nullable = 10, nonNullable = 50), Json.decodeFromString("""{"nullable":12,"nonNullable":52}"""))
+ }
+
+ @Test
+ fun testFileContextual() {
+ val module = SerializersModule {
+ contextual(FileContextualSerializer)
+ }
+
+ val json = Json { serializersModule = module }
+
+ assertEquals("""{"nullable":null,"nonNullable":"foo"}""", json.encodeToString(FileContextualHolder(null, FileContextualType("foo"))))
+ assertEquals("""{"nullable":"foo","nonNullable":"bar"}""", json.encodeToString(FileContextualHolder(FileContextualType("foo"), FileContextualType("bar"))))
+
+ assertEquals(FileContextualHolder(null, FileContextualType("foo")), json.decodeFromString("""{"nullable":null,"nonNullable":"foo"}"""))
+ assertEquals(FileContextualHolder(FileContextualType("foo"), FileContextualType("bar")), json.decodeFromString("""{"nullable":"foo","nonNullable":"bar"}"""))
+ }
+
+ @Test
+ fun testContextual() {
+ val module = SerializersModule {
+ contextual(ContextualSerializer)
+ }
+
+ val json = Json { serializersModule = module }
+
+ assertEquals("""{"nullable":null,"nonNullable":"foo"}""", json.encodeToString(ContextualHolder(null, ContextualType("foo"))))
+ assertEquals("""{"nullable":"foo","nonNullable":"bar"}""", json.encodeToString(ContextualHolder(ContextualType("foo"), ContextualType("bar"))))
+
+ assertEquals(ContextualHolder(null, ContextualType("foo")), json.decodeFromString("""{"nullable":null,"nonNullable":"foo"}"""))
+ assertEquals(ContextualHolder(ContextualType("foo"), ContextualType("bar")), json.decodeFromString("""{"nullable":"foo","nonNullable":"bar"}"""))
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt b/formats/json/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
new file mode 100644
index 00000000..6d4d42f2
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.native.concurrent.*
+
+@Serializable
+open class PolyBase(val id: Int) {
+ override fun hashCode(): Int {
+ return id
+ }
+
+ override fun toString(): String {
+ return "PolyBase(id=$id)"
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+ other as PolyBase
+ if (id != other.id) return false
+ return true
+ }
+
+}
+
+// TODO sandwwraith moving this class to the corresponding tests breaks runtime in unexpected ways
+@Serializable
+data class PolyDefault(val json: JsonElement) : PolyBase(-1)
+
+class PolyDefaultWithId(id: Int) : PolyBase(id)
+
+@Serializable
+data class PolyDerived(val s: String) : PolyBase(1)
+
+@SharedImmutable
+val BaseAndDerivedModule = SerializersModule {
+ polymorphic(PolyBase::class, PolyBase.serializer()) {
+ subclass(PolyDerived.serializer())
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/SerializableClasses.kt b/formats/json/commonTest/src/kotlinx/serialization/SerializableClasses.kt
new file mode 100644
index 00000000..16fdd2fd
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/SerializableClasses.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+@Serializable
+data class IntData(val intV: Int)
+
+@Serializable
+data class StringData(val data: String)
+
+enum class SampleEnum { OptionA, OptionB, OptionC }
+
+@Serializable
+data class Box<T>(val boxed: T)
+
+@Serializable
+sealed class SimpleSealed {
+ @Serializable
+ public data class SubSealedA(val s: String) : SimpleSealed()
+
+ @Serializable
+ public data class SubSealedB(val i: Int) : SimpleSealed()
+}
+
+@Serializable
+object SampleObject {
+ val state: String = "myState"
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/SerializationForNullableTypeOnFileTest.kt b/formats/json/commonTest/src/kotlinx/serialization/SerializationForNullableTypeOnFileTest.kt
new file mode 100644
index 00000000..9f838b11
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/SerializationForNullableTypeOnFileTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:UseSerializers(NullableIntSerializer::class, NonNullableIntSerializer::class)
+
+package kotlinx.serialization
+
+import kotlinx.serialization.SerializationForNullableTypeOnFileTest.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+class SerializationForNullableTypeOnFileTest {
+
+ @Serializable
+ data class Holder(val nullable: Int?, val nonNullable: Int)
+
+ @Serializer(forClass = Int::class)
+ object NullableIntSerializer : KSerializer<Int?> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("NullableIntSerializer", PrimitiveKind.INT).nullable
+
+ override fun serialize(encoder: Encoder, value: Int?) {
+ if (value == null) encoder.encodeNull()
+ else encoder.encodeInt(value + 1)
+ }
+ override fun deserialize(decoder: Decoder): Int? {
+ return if (decoder.decodeNotNullMark()) {
+ val value = decoder.decodeInt()
+ value - 1
+ } else {
+ decoder.decodeNull()
+ }
+ }
+ }
+
+ @Serializer(forClass = Int::class)
+ object NonNullableIntSerializer : KSerializer<Int> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("NotNullIntSerializer", PrimitiveKind.INT)
+
+ override fun serialize(encoder: Encoder, value: Int) {
+ return encoder.encodeInt(value + 2)
+ }
+
+ override fun deserialize(decoder: Decoder): Int {
+ return (decoder.decodeInt() - 2)
+ }
+ }
+
+ @Test
+ fun testFileLevel() {
+ assertEquals("""{"nullable":null,"nonNullable":52}""", Json.encodeToString(Holder(nullable = null, nonNullable = 50)))
+ assertEquals("""{"nullable":1,"nonNullable":2}""", Json.encodeToString(Holder(nullable = 0, nonNullable = 0)))
+ assertEquals("""{"nullable":11,"nonNullable":52}""", Json.encodeToString(Holder(nullable = 10, nonNullable = 50)))
+
+ assertEquals(Holder(nullable = 0, nonNullable = 50), Json.decodeFromString("""{"nullable":1,"nonNullable":52}"""))
+ assertEquals(Holder(nullable = null, nonNullable = 50), Json.decodeFromString("""{"nullable":null,"nonNullable":52}"""))
+ assertEquals(Holder(nullable = 10, nonNullable = 50), Json.decodeFromString("""{"nullable":11,"nonNullable":52}"""))
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/SerializerForNullableTypeTest.kt b/formats/json/commonTest/src/kotlinx/serialization/SerializerForNullableTypeTest.kt
new file mode 100644
index 00000000..64461678
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/SerializerForNullableTypeTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+public class SerializerForNullableTypeTest : JsonTestBase() {
+
+ // Nullable boxes
+ @Serializable(with = StringHolderSerializer::class)
+ data class StringHolder(val s: String)
+
+ @Serializer(forClass = StringHolder::class)
+ object StringHolderSerializer : KSerializer<StringHolder?> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("SHS", PrimitiveKind.STRING).nullable
+
+ override fun serialize(encoder: Encoder, value: StringHolder?) {
+ if (value == null) encoder.encodeString("nullable")
+ else encoder.encodeString("non-nullable")
+ }
+
+ override fun deserialize(decoder: Decoder): StringHolder? {
+ if (decoder.decodeNotNullMark()) {
+ return StringHolder("non-null: " + decoder.decodeString())
+ }
+ decoder.decodeNull()
+ return StringHolder("nullable")
+ }
+ }
+
+ @Serializable
+ data class Box(val s: StringHolder?)
+
+ @Test
+ fun testNullableBoxWithNotNull() {
+ val b = Box(StringHolder("box"))
+ val string = Json.encodeToString(b)
+ assertEquals("""{"s":"non-nullable"}""", string)
+ val deserialized = Json.decodeFromString<Box>(string)
+ assertEquals(Box(StringHolder("non-null: non-nullable")), deserialized)
+ }
+
+ @Test
+ fun testNullableBoxWithNull() {
+ val b = Box(null)
+ val string = Json.encodeToString(b)
+ assertEquals("""{"s":"nullable"}""", string)
+ val deserialized = Json.decodeFromString<Box>(string)
+ assertEquals(Box(StringHolder("non-null: nullable")), deserialized)
+ }
+
+ @Test
+ fun testNullableBoxDeserializeNull() {
+ val deserialized = Json.decodeFromString<Box>("""{"s":null}""")
+ assertEquals(Box(StringHolder("nullable")), deserialized)
+ }
+
+ // Nullable primitives
+ object NullableLongSerializer : KSerializer<Long?> {
+
+ @Serializable
+ data class OptionalLong(val initialized: Boolean, val value: Long? = 0)
+
+ override val descriptor: SerialDescriptor = buildClassSerialDescriptor("NLS") {
+ element<Boolean>("initialized")
+ element<Long?>("value")
+ }.nullable
+
+ override fun serialize(encoder: Encoder, value: Long?) {
+ val opt = OptionalLong(value != null, value)
+ encoder.encodeSerializableValue(OptionalLong.serializer(), opt)
+ }
+
+ override fun deserialize(decoder: Decoder): Long? {
+ val value = decoder.decodeSerializableValue(OptionalLong.serializer())
+ return if (value.initialized) value.value else null
+ }
+ }
+
+ @Serializable
+ data class NullablePrimitive(
+ @Serializable(with = NullableLongSerializer::class) val value: Long?
+ )
+
+ @Test
+ fun testNullableLongWithNotNull() {
+ val data = NullablePrimitive(42)
+ val json = Json.encodeToString(data)
+ assertEquals("""{"value":{"initialized":true,"value":42}}""", Json.encodeToString(data))
+ assertEquals(data, Json.decodeFromString(json))
+ }
+
+ @Test
+ fun testNullableLongWithNull() {
+ val data = NullablePrimitive(null)
+ val json = Json.encodeToString(data)
+ assertEquals("""{"value":{"initialized":false,"value":null}}""", Json.encodeToString(data))
+ assertEquals(data, Json.decodeFromString(json))
+ }
+
+ // Now generics
+ @Serializable
+ data class GenericNullableBox<T: Any>(val value: T?)
+
+ @Serializable
+ data class GenericBox<T>(val value: T?)
+
+ @Test
+ fun testGenericBoxNullable() {
+ if (isJsLegacy()) return
+ val data = GenericBox<StringHolder?>(null)
+ val json = Json.encodeToString(data)
+ assertEquals("""{"value":"nullable"}""", Json.encodeToString(data))
+ assertEquals(GenericBox(StringHolder("non-null: nullable")), Json.decodeFromString(json))
+ }
+
+ @Test
+ fun testGenericNullableBoxFromNull() {
+ if (isJsLegacy()) return
+ assertEquals(GenericBox(StringHolder("nullable")), Json.decodeFromString("""{"value":null}"""))
+ }
+
+ @Test
+ fun testGenericNullableBoxNullable() {
+ if (isJsLegacy()) return
+ val data = GenericNullableBox<StringHolder>(null)
+ val json = Json.encodeToString(data)
+ assertEquals("""{"value":"nullable"}""", Json.encodeToString(data))
+ assertEquals(GenericNullableBox(StringHolder("non-null: nullable")), Json.decodeFromString(json))
+ }
+
+ @Test
+ fun testGenericBoxNullableFromNull() {
+ if (isJsLegacy()) return
+ assertEquals(GenericNullableBox(StringHolder("nullable")), Json.decodeFromString("""{"value":null}"""))
+ }
+
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt b/formats/json/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt
new file mode 100644
index 00000000..18a69404
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.features.sealed.SealedChild
+import kotlinx.serialization.features.sealed.SealedParent
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.reflect.*
+import kotlin.test.*
+
+@Suppress("RemoveExplicitTypeArguments") // This is exactly what's being tested
+class SerializersLookupTest : JsonTestBase() {
+
+ @Test
+ fun testPrimitive() {
+ val token = typeOf<Int>()
+ val serial = serializer(token)
+ assertSame(Int.serializer() as KSerializer<*>, serial)
+ assertSerializedWithType("42", 42)
+ }
+
+ @Test
+ fun testPlainClass() {
+ val b = StringData("some string")
+ assertSerializedWithType("""{"data":"some string"}""", b)
+ }
+
+ @Test
+ fun testListWithT() {
+ val source = """[{"intV":42}]"""
+ val serial = serializer<List<IntData>>()
+ assertEquals(listOf(IntData(42)), Json.decodeFromString(serial, source))
+ }
+
+ @Test
+ fun testPrimitiveList() {
+ val myArr = listOf("a", "b", "c")
+ assertSerializedWithType("""["a","b","c"]""", myArr)
+ }
+
+ @Test
+ fun testListAsCollection() {
+ val myArr: Collection<String> = listOf("a", "b", "c")
+ assertSerializedWithType("""["a","b","c"]""", myArr)
+ }
+
+
+ @Test
+ fun testPrimitiveSet() {
+ val mySet = setOf("a", "b", "c", "c")
+ assertSerializedWithType("""["a","b","c"]""", mySet)
+ }
+
+ @Test
+ fun testMapWithT() {
+ val myMap = mapOf("string" to StringData("foo"), "string2" to StringData("bar"))
+ assertSerializedWithType("""{"string":{"data":"foo"},"string2":{"data":"bar"}}""", myMap)
+ }
+
+ @Test
+ fun testNestedLists() {
+ val myList = listOf(listOf(listOf(1, 2, 3)), listOf())
+ assertSerializedWithType("[[[1,2,3]],[]]", myList)
+ }
+
+ @Test
+ fun testListSubtype() {
+ val myList = arrayListOf(1, 2, 3)
+ assertSerializedWithType<ArrayList<Int>>("[1,2,3]", myList)
+ assertSerializedWithType<List<Int>>("[1,2,3]", myList)
+ }
+
+ @Test
+ fun testListProjection() {
+ val myList = arrayListOf(1, 2, 3)
+ assertSerializedWithType<List<Int>>("[1,2,3]", myList)
+ assertSerializedWithType<MutableList<out Int>>("[1,2,3]", myList)
+ assertSerializedWithType<ArrayList<in Int>>("[1,2,3]", myList)
+ }
+
+ @Test
+ fun testNullableTypes() {
+ val myList: List<Int?> = listOf(1, null, 3)
+ assertSerializedWithType("[1,null,3]", myList)
+ assertSerializedWithType<List<Int?>?>("[1,null,3]", myList)
+ }
+
+ @Test
+ fun testPair() {
+ val myPair = "42" to 42
+ assertSerializedWithType("""{"first":"42","second":42}""", myPair)
+ }
+
+ @Test
+ fun testTriple() = noLegacyJs { // because of Box
+ val myTriple = Triple("1", 2, Box(42))
+ assertSerializedWithType("""{"first":"1","second":2,"third":{"boxed":42}}""", myTriple)
+ }
+
+ @Test
+ fun testCustomGeneric() = noLegacyJs {
+ val intBox = Box(42)
+ val intBoxSerializer = serializer<Box<Int>>()
+ assertEquals(Box.serializer(Int.serializer()).descriptor, intBoxSerializer.descriptor)
+ assertSerializedWithType("""{"boxed":42}""", intBox)
+ val dataBox = Box(StringData("foo"))
+ assertSerializedWithType("""{"boxed":{"data":"foo"}}""", dataBox)
+ }
+
+ @Test
+ fun testRecursiveGeneric() = noLegacyJs {
+ val boxBox = Box(Box(Box(IntData(42))))
+ assertSerializedWithType("""{"boxed":{"boxed":{"boxed":{"intV":42}}}}""", boxBox)
+ }
+
+ @Test
+ fun testMixedGeneric() = noLegacyJs {
+ val listOfBoxes = listOf(Box("foo"), Box("bar"))
+ assertSerializedWithType("""[{"boxed":"foo"},{"boxed":"bar"}]""", listOfBoxes)
+ val boxedList = Box(listOf("foo", "bar"))
+ assertSerializedWithType("""{"boxed":["foo","bar"]}""", boxedList)
+ }
+
+ @Test
+ fun testReferenceArrays() {
+ assertSerializedWithType("[1,2,3]", Array<Int>(3) { it + 1 }, default)
+ assertSerializedWithType("""["1","2","3"]""", Array<String>(3) { (it + 1).toString() }, default)
+ assertSerializedWithType("[[0],[1],[2]]", Array<Array<Int>>(3) { cnt -> Array(1) { cnt } }, default)
+ noLegacyJs {
+ assertSerializedWithType("""[{"boxed":"foo"}]""", Array(1) { Box("foo") }, default)
+ assertSerializedWithType("""[[{"boxed":"foo"}]]""", Array(1) { Array(1) { Box("foo") } }, default)
+ }
+ }
+
+ @Test
+ fun testPrimitiveArrays() {
+ assertSerializedWithType("[1,2,3]", intArrayOf(1, 2, 3), default)
+ assertSerializedWithType("[1,2,3]", longArrayOf(1, 2, 3), default)
+ assertSerializedWithType("[1,2,3]", byteArrayOf(1, 2, 3), default)
+ assertSerializedWithType("[1,2,3]", shortArrayOf(1, 2, 3), default)
+ assertSerializedWithType("[true,false]", booleanArrayOf(true, false), default)
+ assertSerializedWithType("""["a","b","c"]""", charArrayOf('a', 'b', 'c'), default)
+ }
+
+ @Test
+ fun testSerializableObject() = noLegacyJs {
+ assertSerializedWithType("{}", SampleObject)
+ }
+
+ class IntBox(val i: Int)
+
+ class CustomIntSerializer(isNullable: Boolean) : KSerializer<IntBox?> {
+ override val descriptor: SerialDescriptor
+
+ init {
+ val d = PrimitiveSerialDescriptor("CIS", PrimitiveKind.INT)
+ descriptor = if (isNullable) d.nullable else d
+ }
+
+ override fun serialize(encoder: Encoder, value: IntBox?) {
+ if (value == null) encoder.encodeInt(41)
+ else encoder.encodeInt(42)
+ }
+
+ override fun deserialize(decoder: Decoder): IntBox? {
+ TODO()
+ }
+ }
+
+ @Test
+ fun testContextualLookup() {
+ val module = SerializersModule { contextual(CustomIntSerializer(false).cast<IntBox>()) }
+ val json = Json { serializersModule = module }
+ val data = listOf(listOf(IntBox(1)))
+ assertEquals("[[42]]", json.encodeToString(data))
+ }
+
+ @Test
+ fun testContextualLookupNullable() {
+ val module = SerializersModule { contextual(CustomIntSerializer(true).cast<IntBox>()) }
+ val serializer = module.serializer<List<List<IntBox?>>>()
+ assertEquals("[[41]]", Json.encodeToString(serializer, listOf(listOf<IntBox?>(null))))
+ }
+
+ @Test
+ fun testContextualLookupNonNullable() {
+ val module = SerializersModule { contextual(CustomIntSerializer(false).cast<IntBox>()) }
+ val serializer = module.serializer<List<List<IntBox?>>>()
+ assertEquals("[[null]]", Json.encodeToString(serializer, listOf(listOf<IntBox?>(null))))
+ }
+
+ @Test
+ fun testCompiledWinsOverContextual() {
+ val contextual = object : KSerializer<Int> {
+ override val descriptor: SerialDescriptor = Int.serializer().descriptor
+
+ override fun serialize(encoder: Encoder, value: Int) {
+ fail()
+ }
+
+ override fun deserialize(decoder: Decoder): Int {
+ fail()
+ }
+ }
+ val json = Json { serializersModule = SerializersModule { contextual(contextual) } }
+ assertEquals("[[1]]", json.encodeToString(listOf(listOf<Int>(1))))
+ assertEquals("42", json.encodeToString(42))
+ }
+
+ class NonSerializable
+
+ class NonSerializableBox<T>(val boxed: T)
+
+ @Test
+ fun testSealedFromOtherFileLookup() {
+ assertNotNull(serializerOrNull(typeOf<SealedParent>()))
+ assertNotNull(serializerOrNull(typeOf<SealedChild>()))
+ }
+
+ @Test
+ fun testLookupFail() {
+ assertNull(serializerOrNull(typeOf<NonSerializable>()))
+ assertNull(serializerOrNull(typeOf<NonSerializableBox<String>>()))
+ assertNull(serializerOrNull(typeOf<Box<NonSerializable>>()))
+
+ assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") {
+ serializer(typeOf<NonSerializable>())
+ }
+
+ assertFailsWithMessage<SerializationException>("for class 'NonSerializableBox'") {
+ serializer(typeOf<NonSerializableBox<String>>())
+ }
+
+ assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") {
+ serializer(typeOf<Box<NonSerializable>>())
+ }
+ }
+
+ private inline fun <reified T> assertSerializedWithType(
+ expected: String,
+ value: T,
+ json: StringFormat = default
+ ) {
+ val serial = serializer<T>()
+ assertEquals(expected, json.encodeToString(serial, value))
+ val serial2 = requireNotNull(serializerOrNull(typeOf<T>())) { "Expected serializer to be found" }
+ assertEquals(expected, json.encodeToString(serial2, value))
+ }
+
+ @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
+ inline fun <T> KSerializer<*>.cast(): KSerializer<T> = this as KSerializer<T>
+
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/TuplesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/TuplesTest.kt
new file mode 100644
index 00000000..9fd2d5ea
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/TuplesTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.test.assertStringFormAndRestored
+import kotlin.test.*
+
+class TuplesTest : JsonTestBase() {
+ @Serializable
+ data class MyPair<K, V>(val k: K, val v: V)
+
+ @Serializable
+ data class PairWrapper(val p: Pair<Int, String>)
+
+ @Serializable
+ data class TripleWrapper(val t: Triple<Int, String, Boolean>)
+
+ @Test
+ fun testCustomPair() = assertStringFormAndRestored(
+ """{"k":42,"v":"foo"}""",
+ MyPair(42, "foo"),
+ MyPair.serializer(
+ Int.serializer(),
+ String.serializer()
+ ),
+ lenient
+ )
+
+ @Test
+ fun testStandardPair() = assertStringFormAndRestored(
+ """{"p":{"first":42,"second":"foo"}}""",
+ PairWrapper(42 to "foo"),
+ PairWrapper.serializer(),
+ lenient
+ )
+
+ @Test
+ fun testStandardPairHasCorrectDescriptor() {
+ val desc = PairWrapper.serializer().descriptor.getElementDescriptor(0)
+ assertEquals(desc.serialName, "kotlin.Pair")
+ assertEquals(
+ desc.elementDescriptors.map(SerialDescriptor::kind),
+ listOf(PrimitiveKind.INT, PrimitiveKind.STRING)
+ )
+ }
+
+ @Test
+ fun testStandardTriple() = assertStringFormAndRestored(
+ """{"t":{"first":42,"second":"foo","third":false}}""",
+ TripleWrapper(Triple(42, "foo", false)),
+ TripleWrapper.serializer(),
+ lenient
+ )
+
+ @Test
+ fun testStandardTripleHasCorrectDescriptor() {
+ val desc = TripleWrapper.serializer().descriptor.getElementDescriptor(0)
+ assertEquals(desc.serialName, "kotlin.Triple")
+ assertEquals(
+ desc.elementDescriptors.map(SerialDescriptor::kind),
+ listOf(PrimitiveKind.INT, PrimitiveKind.STRING, PrimitiveKind.BOOLEAN)
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/UmbrellaTypes.kt b/formats/json/commonTest/src/kotlinx/serialization/UmbrellaTypes.kt
new file mode 100644
index 00000000..5e6432ea
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/UmbrellaTypes.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlin.native.concurrent.*
+
+enum class Attitude { POSITIVE, NEUTRAL, NEGATIVE }
+
+@Serializable
+data class Tree(val name: String, val left: Tree? = null, val right: Tree? = null)
+
+@Serializable
+data class TypesUmbrella(
+ val unit: Unit,
+ val boolean: Boolean,
+ val byte: Byte,
+ val short: Short,
+ val int: Int,
+ val long: Long,
+ val float: Float,
+ val double: Double,
+ val char: Char,
+ val string: String,
+ val enum: Attitude,
+ val intData: IntData,
+ val unitN: Unit?,
+ val booleanN: Boolean?,
+ val byteN: Byte?,
+ val shortN: Short?,
+ val intN: Int?,
+ val longN: Long?,
+ val floatN: Float?,
+ val doubleN: Double?,
+ val charN: Char?,
+ val stringN: String?,
+ val enumN: Attitude?,
+ val intDataN: IntData?,
+ val listInt: List<Int>,
+ val listIntN: List<Int?>,
+ val listNInt: Set<Int>?,
+ val listNIntN: MutableSet<Int?>?,
+ val listListEnumN: List<List<Attitude?>>,
+ val listIntData: List<IntData>,
+ val listIntDataN: MutableList<IntData?>,
+ val tree: Tree,
+ val mapStringInt: Map<String, Int>,
+ val mapIntStringN: Map<Int, String?>,
+ val arrays: ArraysUmbrella
+)
+
+@Serializable
+data class ArraysUmbrella(
+ val arrByte: Array<Byte>,
+ val arrInt: Array<Int>,
+ val arrIntN: Array<Int?>,
+ val arrIntData: Array<IntData>
+) {
+ override fun equals(other: Any?) = other is ArraysUmbrella &&
+ arrByte.contentEquals(other.arrByte) &&
+ arrInt.contentEquals(other.arrInt) &&
+ arrIntN.contentEquals(other.arrIntN) &&
+ arrIntData.contentEquals(other.arrIntData)
+}
+
+@SharedImmutable
+val umbrellaInstance = TypesUmbrella(
+ Unit, true, 10, 20, 30, 40, 50.1f, 60.1, 'A', "Str0", Attitude.POSITIVE, IntData(70),
+ null, null, 11, 21, 31, 41, 51.1f, 61.1, 'B', "Str1", Attitude.NEUTRAL, null,
+ listOf(1, 2, 3),
+ listOf(4, 5, null),
+ setOf(6, 7, 8),
+ mutableSetOf(null, 9, 10),
+ listOf(listOf(Attitude.NEGATIVE, null)),
+ listOf(IntData(1), IntData(2), IntData(3)),
+ mutableListOf(IntData(1), null, IntData(3)),
+ Tree("root", Tree("left"), Tree("right", Tree("right.left"), Tree("right.right"))),
+ mapOf("one" to 1, "two" to 2, "three" to 3),
+ mapOf(0 to null, 1 to "first", 2 to "second"),
+ ArraysUmbrella(
+ arrayOf(1, 2, 3),
+ arrayOf(100, 200, 300),
+ arrayOf(null, -1, -2),
+ arrayOf(IntData(1), IntData(2))
+ )
+)
diff --git a/formats/json/commonTest/src/kotlinx/serialization/UnknownElementIndexTest.kt b/formats/json/commonTest/src/kotlinx/serialization/UnknownElementIndexTest.kt
new file mode 100644
index 00000000..81a44ef2
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/UnknownElementIndexTest.kt
@@ -0,0 +1,39 @@
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.modules.*
+import kotlin.test.Test
+import kotlin.test.assertFailsWith
+
+class UnknownElementIndexTest {
+ enum class Choices { A, B, C }
+
+ @Serializable
+ data class Holder(val c: Choices)
+
+ class MalformedReader : AbstractDecoder() {
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ return UNKNOWN_NAME
+ }
+ }
+
+ @Test
+ fun testCompilerComplainsAboutIncorrectIndex() {
+ assertFailsWith(SerializationException::class) {
+ MalformedReader().decodeSerializableValue(Holder.serializer())
+ }
+ }
+
+ @Test
+ fun testErrorMessage() {
+ val message = "kotlinx.serialization.UnknownElementIndexTest.Choices does not contain element with name 'D'"
+ assertFailsWith(SerializationException::class, message) {
+ Json.decodeFromString(Holder.serializer(), """{"c":"D"}""")
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/builtins/KeyValueSerializersTest.kt b/formats/json/commonTest/src/kotlinx/serialization/builtins/KeyValueSerializersTest.kt
new file mode 100644
index 00000000..c6a1bbe3
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/builtins/KeyValueSerializersTest.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.builtins
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class KeyValueSerializersTest : JsonTestBase() {
+
+ @Test
+ fun testPair() = parametrizedTest { jsonTestingMode ->
+ testPair(Pair(42, 42), Int.serializer(), Int.serializer(), jsonTestingMode, """{"first":42,"second":42}""")
+ testPair(
+ Pair(42, Pair("a", "b")),
+ Int.serializer(),
+ serializer(),
+ jsonTestingMode,
+ """{"first":42,"second":{"first":"a","second":"b"}}"""
+ )
+ testPair(
+ Pair(42, null),
+ Int.serializer(),
+ Int.serializer().nullable,
+ jsonTestingMode,
+ """{"first":42,"second":null}"""
+ )
+ }
+
+ private fun <K, V> testPair(
+ pairInstance: Pair<K, V>,
+ kSerializer: KSerializer<K>,
+ vSerializer: KSerializer<V>,
+ jsonTestingMode: JsonTestingMode,
+ expectedJson: String
+ ) {
+ val serializer = PairSerializer(kSerializer, vSerializer)
+ val json = default.encodeToString(serializer, pairInstance, jsonTestingMode)
+ assertEquals(expectedJson, json)
+ val pair = default.decodeFromString(serializer, json, jsonTestingMode)
+ assertEquals(pairInstance, pair)
+ }
+
+ @Test
+ fun testTriple() = parametrizedTest { jsonTestingMode ->
+ testTriple(
+ Triple(42, 42, "42"),
+ Int.serializer(),
+ Int.serializer(),
+ String.serializer(),
+ jsonTestingMode,
+ """{"first":42,"second":42,"third":"42"}"""
+ )
+
+ testTriple(
+ Triple(42, Triple(42, "f", 'c'), "42"),
+ Int.serializer(),
+ serializer(),
+ String.serializer(),
+ jsonTestingMode,
+ """{"first":42,"second":{"first":42,"second":"f","third":"c"},"third":"42"}"""
+ )
+
+ testTriple(
+ Triple(42, null, null),
+ Int.serializer(),
+ Int.serializer().nullable,
+ String.serializer().nullable,
+ jsonTestingMode,
+ """{"first":42,"second":null,"third":null}"""
+ )
+ }
+
+ private fun <A, B, C> testTriple(
+ tripleInstance: Triple<A, B, C>,
+ aSerializer: KSerializer<A>,
+ bSerializer: KSerializer<B>,
+ cSerializer: KSerializer<C>,
+ jsonTestingMode: JsonTestingMode,
+ expectedJson: String
+ ) {
+ val serializer = TripleSerializer(aSerializer, bSerializer, cSerializer)
+ val json = default.encodeToString(serializer, tripleInstance, jsonTestingMode)
+ assertEquals(expectedJson, json)
+ val triple = default.decodeFromString(serializer, json, jsonTestingMode)
+ assertEquals(tripleInstance, triple)
+ }
+
+ class Entry<K, V>(override val key: K, override val value: V) : Map.Entry<K, V> {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other !is Map.Entry<*, *>) return false
+ if (key != other.key) return false
+ if (value != other.value) return false
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = key?.hashCode() ?: 0
+ result = 31 * result + (value?.hashCode() ?: 0)
+ return result
+ }
+ }
+
+ @Test
+ fun testKeyValuePair() = parametrizedTest { jsonTestingMode ->
+ jvmOnly {
+ testEntry(Entry(42, 42), Int.serializer(), Int.serializer(), jsonTestingMode, """{"42":42}""")
+ testEntry(
+ Entry(42, Entry("a", "b")),
+ Int.serializer(),
+ serializer<Map.Entry<String, String>>(),
+ jsonTestingMode,
+ """{"42":{"a":"b"}}"""
+ )
+ testEntry(
+ Entry(42, null),
+ Int.serializer(),
+ Int.serializer().nullable,
+ jsonTestingMode,
+ """{"42":null}"""
+ )
+ }
+ }
+
+ private inline fun <reified K, reified V> testEntry(
+ entryInstance: Map.Entry<K, V>,
+ kSerializer: KSerializer<K>,
+ vSerializer: KSerializer<V>,
+ jsonTestingMode: JsonTestingMode,
+ expectedJson: String
+ ) {
+ val serializer = MapEntrySerializer(kSerializer, vSerializer)
+ val json = default.encodeToString(serializer, entryInstance, jsonTestingMode)
+ assertEquals(expectedJson, json)
+ val entry = default.decodeFromString(serializer, json, jsonTestingMode)
+ assertEquals(entryInstance, entry)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/BinaryPayloadExampleTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/BinaryPayloadExampleTest.kt
new file mode 100644
index 00000000..c1a98735
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/BinaryPayloadExampleTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.test.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class BinaryPayloadExampleTest {
+ @Serializable
+ class BinaryPayload(val req: ByteArray, val res: ByteArray) {
+ @Serializer(forClass = BinaryPayload::class)
+ companion object : KSerializer<BinaryPayload> {
+ override val descriptor: SerialDescriptor = buildClassSerialDescriptor("BinaryPayload") {
+ element("req", ByteArraySerializer().descriptor)
+ element("res", ByteArraySerializer().descriptor)
+ }
+
+ override fun serialize(encoder: Encoder, value: BinaryPayload) {
+ val compositeOutput = encoder.beginStructure(descriptor)
+ compositeOutput.encodeStringElement(descriptor, 0, InternalHexConverter.printHexBinary(value.req))
+ compositeOutput.encodeStringElement(descriptor, 1, InternalHexConverter.printHexBinary(value.res))
+ compositeOutput.endStructure(descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): BinaryPayload {
+ val dec: CompositeDecoder = decoder.beginStructure(descriptor)
+ var req: ByteArray? = null // consider using flags or bit mask if you
+ var res: ByteArray? = null // need to read nullable non-optional properties
+ loop@ while (true) {
+ when (val i = dec.decodeElementIndex(descriptor)) {
+ CompositeDecoder.DECODE_DONE -> break@loop
+ 0 -> req = InternalHexConverter.parseHexBinary(dec.decodeStringElement(descriptor, i))
+ 1 -> res = InternalHexConverter.parseHexBinary(dec.decodeStringElement(descriptor, i))
+ else -> throw SerializationException("Unknown index $i")
+ }
+ }
+ dec.endStructure(descriptor)
+ return BinaryPayload(
+ req ?: throw SerializationException("MFE: req"),
+ res ?: throw SerializationException("MFE: res")
+ )
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as BinaryPayload
+
+ if (!req.contentEquals(other.req)) return false
+ if (!res.contentEquals(other.res)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = req.contentHashCode()
+ result = 31 * result + res.contentHashCode()
+ return result
+ }
+ }
+
+ @Test
+ fun payloadEquivalence() {
+ val payload1 = BinaryPayload(byteArrayOf(0, 0, 0), byteArrayOf(127, 127))
+ val s = Json.encodeToString(BinaryPayload.serializer(), payload1)
+ val payload2 = Json.decodeFromString(BinaryPayload.serializer(), s)
+ assertEquals(payload1, payload2)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/ByteArraySerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/ByteArraySerializerTest.kt
new file mode 100644
index 00000000..aa1ad2d0
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/ByteArraySerializerTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class ByteArraySerializerTest {
+
+ @Serializable
+ class ByteArrayCarrier(@Id(2) val data: ByteArray) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as ByteArrayCarrier
+
+ if (!data.contentEquals(other.data)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ return data.contentHashCode()
+ }
+
+ override fun toString(): String {
+ return "ByteArrayCarrier(data=${data.contentToString()})"
+ }
+ }
+
+ @Test
+ fun testByteArrayJson() {
+ val bytes = byteArrayOf(42, 43, 44, 45)
+ val s = Json.encodeToString(ByteArraySerializer(), bytes)
+ assertEquals(s, """[42,43,44,45]""")
+ val bytes2 = Json.decodeFromString(ByteArraySerializer(), s)
+ assertTrue(bytes.contentEquals(bytes2))
+ }
+
+ @Test
+ fun testWrappedByteArrayJson() {
+ val obj = ByteArrayCarrier(byteArrayOf(42, 100))
+ val s = Json.encodeToString(ByteArrayCarrier.serializer(), obj)
+ assertEquals("""{"data":[42,100]}""", s)
+ val obj2 = Json.decodeFromString(ByteArrayCarrier.serializer(), s)
+ assertEquals(obj, obj2)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/CollectionSerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/CollectionSerializerTest.kt
new file mode 100644
index 00000000..ca8116a0
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/CollectionSerializerTest.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class CollectionSerializerTest {
+
+ @Serializable
+ data class CollectionWrapper(
+ val collection: Collection<String>
+ )
+
+ @Test
+ fun testListJson() {
+ val list = listOf("foo", "bar", "foo", "bar")
+
+ val string = Json.encodeToString(CollectionWrapper(list))
+ assertEquals("""{"collection":["foo","bar","foo","bar"]}""", string)
+
+ val wrapper = Json.decodeFromString<CollectionWrapper>(string)
+ assertEquals(list, wrapper.collection)
+ }
+
+ @Test
+ fun testSetJson() {
+ val set = setOf("foo", "bar", "foo", "bar")
+
+ val string = Json.encodeToString(CollectionWrapper(set))
+ assertEquals("""{"collection":["foo","bar"]}""", string)
+
+ val wrapper = Json.decodeFromString<CollectionWrapper>(string)
+ assertEquals(set.toList(), wrapper.collection)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/ContextAndPolymorphicTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/ContextAndPolymorphicTest.kt
new file mode 100644
index 00000000..8f00ad97
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/ContextAndPolymorphicTest.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class ContextAndPolymorphicTest {
+
+ @Serializable
+ data class Data(val a: Int, val b: Int = 42)
+
+ @Serializable
+ data class EnhancedData(
+ val data: Data,
+ @Contextual val stringPayload: Payload,
+ @Serializable(with = BinaryPayloadSerializer::class) val binaryPayload: Payload
+ )
+
+ @Serializable
+ @SerialName("Payload")
+ data class Payload(val s: String)
+
+ @Serializable
+ data class PayloadList(val ps: List<@Contextual Payload>)
+
+ @Serializer(forClass = Payload::class)
+ object PayloadSerializer
+
+ object BinaryPayloadSerializer : KSerializer<Payload> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BinaryPayload", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: Payload) {
+ encoder.encodeString(InternalHexConverter.printHexBinary(value.s.encodeToByteArray()))
+ }
+
+ override fun deserialize(decoder: Decoder): Payload {
+ return Payload(InternalHexConverter.parseHexBinary(decoder.decodeString()).decodeToString())
+ }
+ }
+
+ private val value = EnhancedData(Data(100500), Payload("string"), Payload("binary"))
+ private lateinit var json: Json
+
+ @BeforeTest
+ fun initContext() {
+ val scope = serializersModuleOf(Payload::class, PayloadSerializer)
+ val bPolymorphicModule = SerializersModule { polymorphic(Any::class) { subclass(PayloadSerializer) } }
+ json = Json {
+ useArrayPolymorphism = true
+ encodeDefaults = true
+ serializersModule = scope + bPolymorphicModule
+ }
+ }
+
+ @Test
+ fun testWriteCustom() {
+ val s = json.encodeToString(EnhancedData.serializer(), value)
+ assertEquals("""{"data":{"a":100500,"b":42},"stringPayload":{"s":"string"},"binaryPayload":"62696E617279"}""", s)
+ }
+
+ @Test
+ fun testReadCustom() {
+ val s = json.decodeFromString(EnhancedData.serializer(),
+ """{"data":{"a":100500,"b":42},"stringPayload":{"s":"string"},"binaryPayload":"62696E617279"}""")
+ assertEquals(value, s)
+ }
+
+ @Test
+ fun testWriteCustomList() {
+ val s = json.encodeToString(PayloadList.serializer(), PayloadList(listOf(Payload("1"), Payload("2"))))
+ assertEquals("""{"ps":[{"s":"1"},{"s":"2"}]}""", s)
+ }
+
+ @Test
+ fun testPolymorphicResolve() {
+ val map = mapOf<String, Any>("Payload" to Payload("data"))
+ val serializer = MapSerializer(String.serializer(), PolymorphicSerializer(Any::class))
+ val s = json.encodeToString(serializer, map)
+ assertEquals("""{"Payload":["Payload",{"s":"data"}]}""", s)
+ }
+
+ @Test
+ fun testDifferentRepresentations() {
+ val simpleModule = serializersModuleOf(PayloadSerializer)
+ val binaryModule = serializersModuleOf(BinaryPayloadSerializer)
+
+ val json1 = Json { useArrayPolymorphism = true; serializersModule = simpleModule }
+ val json2 = Json { useArrayPolymorphism = true; serializersModule = binaryModule }
+
+ // in json1, Payload would be serialized with PayloadSerializer,
+ // in json2, Payload would be serialized with BinaryPayloadSerializer
+
+ val list = PayloadList(listOf(Payload("string")))
+ assertEquals("""{"ps":[{"s":"string"}]}""", json1.encodeToString(PayloadList.serializer(), list))
+ assertEquals("""{"ps":["737472696E67"]}""", json2.encodeToString(PayloadList.serializer(), list))
+ }
+
+ private fun SerialDescriptor.inContext(module: SerializersModule): SerialDescriptor = when (kind) {
+ SerialKind.CONTEXTUAL -> requireNotNull(module.getContextualDescriptor(this)) { "Expected $this to be registered in module" }
+ else -> error("Expected this function to be called on CONTEXTUAL descriptor")
+ }
+
+ @Test
+ fun testResolveContextualDescriptor() {
+ val simpleModule = serializersModuleOf(PayloadSerializer)
+ val binaryModule = serializersModuleOf(BinaryPayloadSerializer)
+
+ val contextDesc = EnhancedData.serializer().descriptor.elementDescriptors.toList()[1] // @ContextualSer stringPayload
+ assertEquals(SerialKind.CONTEXTUAL, contextDesc.kind)
+ assertEquals(0, contextDesc.elementsCount)
+
+ val resolvedToDefault = contextDesc.inContext(simpleModule)
+ assertEquals(StructureKind.CLASS, resolvedToDefault.kind)
+ assertEquals("Payload", resolvedToDefault.serialName)
+ assertEquals(1, resolvedToDefault.elementsCount)
+
+ val resolvedToBinary = contextDesc.inContext(binaryModule)
+ assertEquals(PrimitiveKind.STRING, resolvedToBinary.kind)
+ assertEquals("BinaryPayload", resolvedToBinary.serialName)
+ }
+
+ @Test
+ fun testContextualSerializerUsesDefaultIfModuleIsEmpty() {
+ val s = Json { useArrayPolymorphism = true; encodeDefaults = true }.encodeToString(EnhancedData.serializer(), value)
+ assertEquals("""{"data":{"a":100500,"b":42},"stringPayload":{"s":"string"},"binaryPayload":"62696E617279"}""", s)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/DerivedContextualSerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/DerivedContextualSerializerTest.kt
new file mode 100644
index 00000000..7da16fb8
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/DerivedContextualSerializerTest.kt
@@ -0,0 +1,49 @@
+package kotlinx.serialization.features
+
+import kotlin.test.*
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+
+class DerivedContextualSerializerTest {
+
+ @Serializable
+ abstract class Message
+
+ @Serializable
+ class SimpleMessage(val body: String) : Message()
+
+ @Serializable
+ class Holder(@Contextual val message: Message)
+
+ object MessageAsStringSerializer : KSerializer<Message> {
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("kotlinx.serialization.MessageAsStringSerializer", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: Message) {
+ // dummy serializer that assumes Message is always SimpleMessage
+ check(value is SimpleMessage)
+ encoder.encodeString(value.body)
+ }
+
+ override fun deserialize(decoder: Decoder): Message {
+ return SimpleMessage(decoder.decodeString())
+ }
+ }
+
+ @Test
+ fun testDerivedContextualSerializer() {
+ val module = SerializersModule {
+ contextual(MessageAsStringSerializer)
+ }
+ val format = Json { serializersModule = module }
+ val data = Holder(SimpleMessage("hello"))
+ val serialized = format.encodeToString(data)
+ assertEquals("""{"message":"hello"}""", serialized)
+ val deserialized = format.decodeFromString<Holder>(serialized)
+ assertTrue(deserialized.message is SimpleMessage)
+ assertEquals("hello", deserialized.message.body)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/GenericCustomSerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/GenericCustomSerializerTest.kt
new file mode 100644
index 00000000..804dca6c
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/GenericCustomSerializerTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class CheckedData<T : Any>(val data: T, val checkSum: ByteArray) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as CheckedData<*>
+
+ if (data != other.data) return false
+ if (!checkSum.contentEquals(other.checkSum)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = data.hashCode()
+ result = 31 * result + checkSum.contentHashCode()
+ return result
+ }
+}
+
+@Serializer(forClass = CheckedData::class)
+class CheckedDataSerializer<T : Any>(private val dataSerializer: KSerializer<T>) : KSerializer<CheckedData<T>> {
+ override val descriptor: SerialDescriptor = buildClassSerialDescriptor("CheckedDataSerializer") {
+ val dataDescriptor = dataSerializer.descriptor
+ element("data", dataDescriptor)
+ element("checkSum", ByteArraySerializer().descriptor)
+ }
+
+ override fun serialize(encoder: Encoder, value: CheckedData<T>) {
+ val out = encoder.beginStructure(descriptor)
+ out.encodeSerializableElement(descriptor, 0, dataSerializer, value.data)
+ out.encodeStringElement(descriptor, 1, InternalHexConverter.printHexBinary(value.checkSum))
+ out.endStructure(descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): CheckedData<T> {
+ val inp = decoder.beginStructure(descriptor)
+ lateinit var data: T
+ lateinit var sum: ByteArray
+ loop@ while (true) {
+ when (val i = inp.decodeElementIndex(descriptor)) {
+ CompositeDecoder.DECODE_DONE -> break@loop
+ 0 -> data = inp.decodeSerializableElement(descriptor, i, dataSerializer)
+ 1 -> sum = InternalHexConverter.parseHexBinary(inp.decodeStringElement(descriptor, i))
+ else -> throw SerializationException("Unknown index $i")
+ }
+ }
+ inp.endStructure(descriptor)
+ return CheckedData(data, sum)
+ }
+}
+
+@Serializable
+data class DataWithString(@Serializable(with = CheckedDataSerializer::class) val data: CheckedData<String>)
+
+@Serializable
+data class DataWithInt(@Serializable(with = CheckedDataSerializer::class) val data: CheckedData<Int>)
+
+@Serializable
+data class DataWithStringContext(@Contextual val data: CheckedData<String>)
+
+
+class GenericCustomSerializerTest {
+ @Test
+ fun testStringData() {
+ val original = DataWithString(CheckedData("my data", byteArrayOf(42, 32)))
+ val s = Json.encodeToString(DataWithString.serializer(), original)
+ assertEquals("""{"data":{"data":"my data","checkSum":"2A20"}}""", s)
+ val restored = Json.decodeFromString(DataWithString.serializer(), s)
+ assertEquals(original, restored)
+ }
+
+ @Test
+ fun testIntData() {
+ val original = DataWithInt(CheckedData(42, byteArrayOf(42)))
+ val s = Json.encodeToString(DataWithInt.serializer(), original)
+ assertEquals("""{"data":{"data":42,"checkSum":"2A"}}""", s)
+ val restored = Json.decodeFromString(DataWithInt.serializer(), s)
+ assertEquals(original, restored)
+ }
+
+
+ @Test
+ fun testContextualGeneric() {
+ val module = SerializersModule {
+ @Suppress("UNCHECKED_CAST")
+ contextual(CheckedData::class) { args -> CheckedDataSerializer(args[0] as KSerializer<Any>)}
+ }
+ assertStringFormAndRestored(
+ """{"data":{"data":"my data","checkSum":"2A20"}}""",
+ DataWithStringContext(CheckedData("my data", byteArrayOf(42, 32))),
+ DataWithStringContext.serializer(),
+ Json { serializersModule = module }
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/InheritanceTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/InheritanceTest.kt
new file mode 100644
index 00000000..ea11f9b3
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/InheritanceTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("EqualsOrHashCode")
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+@Serializable
+abstract class AbstractSerializable {
+ public abstract val rootState: String // no backing field
+
+ val publicState: String = "A"
+}
+
+@Serializable
+open class SerializableBase: AbstractSerializable() {
+
+
+ private val privateState: String = "B" // still should be serialized
+
+ @Transient
+ private val privateTransientState = "C" // not serialized: explicitly transient
+
+ val notAState: String // not serialized: no backing field
+ get() = "D"
+
+ override val rootState: String
+ get() = "E" // still not serializable
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is SerializableBase) return false
+
+ if (privateState != other.privateState) return false
+ if (privateTransientState != other.privateTransientState) return false
+
+ return true
+ }
+}
+
+@Serializable
+class Derived(val derivedState: Int): SerializableBase() {
+ override val rootState: String = "foo" // serializable!
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is Derived) return false
+ if (!super.equals(other)) return false
+
+ if (derivedState != other.derivedState) return false
+ if (rootState != other.rootState) return false
+
+ return true
+ }
+}
+
+@Serializable
+open class Base1(open var state1: String) {
+ override fun toString(): String {
+ return "Base1(state1='$state1')"
+ }
+}
+
+@Serializable
+class Derived2(@SerialName("state2") override var state1: String): Base1(state1) {
+ override fun toString(): String {
+ return "Derived2(state1='$state1')"
+ }
+}
+
+class InheritanceTest {
+ private val json = Json { encodeDefaults = true }
+
+ @Test
+ fun canBeSerializedAsDerived() {
+ val derived = Derived(42)
+ val msg = json.encodeToString(Derived.serializer(), derived)
+ assertEquals("""{"publicState":"A","privateState":"B","derivedState":42,"rootState":"foo"}""", msg)
+ val d2 = json.decodeFromString(Derived.serializer(), msg)
+ assertEquals(derived, d2)
+ }
+
+ @Test
+ fun canBeSerializedAsParent() {
+ val derived = Derived(42)
+ val msg = json.encodeToString(SerializableBase.serializer(), derived)
+ assertEquals("""{"publicState":"A","privateState":"B"}""", msg)
+ val d2 = json.decodeFromString(SerializableBase.serializer(), msg)
+ assertEquals(SerializableBase(), d2)
+ // no derivedState
+ assertFailsWithMissingField { json.decodeFromString(Derived.serializer(), msg) }
+ }
+
+ @Test
+ fun testWithOpenProperty() {
+ val d = Derived2("foo")
+ val msgFull = json.encodeToString(Derived2.serializer(), d)
+ assertEquals("""{"state1":"foo","state2":"foo"}""", msgFull)
+ assertEquals("""{"state1":"foo"}""", json.encodeToString(Base1.serializer(), d))
+ val restored = json.decodeFromString(Derived2.serializer(), msgFull)
+ val restored2 = json.decodeFromString(Derived2.serializer(), """{"state1":"bar","state2":"foo"}""") // state1 is ignored anyway
+ assertEquals("""Derived2(state1='foo')""", restored.toString())
+ assertEquals("""Derived2(state1='foo')""", restored2.toString())
+ }
+}
+
+
+
+
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/JsonClassDiscriminatorTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/JsonClassDiscriminatorTest.kt
new file mode 100644
index 00000000..93719c31
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/JsonClassDiscriminatorTest.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.noLegacyJs
+import kotlin.test.*
+
+class JsonClassDiscriminatorTest : JsonTestBase() {
+ @Serializable
+ @JsonClassDiscriminator("sealedType")
+ sealed class SealedMessage {
+ @Serializable
+ @SerialName("SealedMessage.StringMessage")
+ data class StringMessage(val description: String, val message: String) : SealedMessage()
+
+ @SerialName("EOF")
+ @Serializable
+ object EOF : SealedMessage()
+ }
+
+ @Serializable
+ @JsonClassDiscriminator("abstractType")
+ abstract class AbstractMessage {
+ @Serializable
+ @SerialName("Message.StringMessage")
+ data class StringMessage(val description: String, val message: String) : AbstractMessage()
+
+ @Serializable
+ @SerialName("Message.IntMessage")
+ data class IntMessage(val description: String, val message: Int) : AbstractMessage()
+ }
+
+
+ @Test
+ fun testSealedClassesHaveCustomDiscriminator() = noLegacyJs {
+ val messages = listOf(
+ SealedMessage.StringMessage("string message", "foo"),
+ SealedMessage.EOF
+ )
+ val expected =
+ """[{"sealedType":"SealedMessage.StringMessage","description":"string message","message":"foo"},{"sealedType":"EOF"}]"""
+ assertJsonFormAndRestored(
+ ListSerializer(SealedMessage.serializer()),
+ messages,
+ expected,
+ )
+ }
+
+ @Test
+ fun testAbstractClassesHaveCustomDiscriminator() = noLegacyJs {
+ val messages = listOf(
+ AbstractMessage.StringMessage("string message", "foo"),
+ AbstractMessage.IntMessage("int message", 42),
+ )
+ val module = SerializersModule {
+ polymorphic(AbstractMessage::class) {
+ subclass(AbstractMessage.StringMessage.serializer())
+ subclass(AbstractMessage.IntMessage.serializer())
+ }
+ }
+ val json = Json { serializersModule = module }
+ val expected =
+ """[{"abstractType":"Message.StringMessage","description":"string message","message":"foo"},{"abstractType":"Message.IntMessage","description":"int message","message":42}]"""
+ assertJsonFormAndRestored(ListSerializer(AbstractMessage.serializer()), messages, expected, json)
+ }
+
+ @Serializable
+ @JsonClassDiscriminator("message_type")
+ abstract class Base
+
+ @Serializable
+ abstract class ErrorClass : Base()
+
+ @Serializable
+ data class Message(val message: Base, val error: ErrorClass?)
+
+ @Serializable
+ @SerialName("my.app.BaseMessage")
+ data class BaseMessage(val message: String) : Base()
+
+ @Serializable
+ @SerialName("my.app.GenericError")
+ data class GenericError(@SerialName("error_code") val errorCode: Int) : ErrorClass()
+
+
+ @Test
+ fun testDocumentationInheritanceSample() = noLegacyJs {
+ val module = SerializersModule {
+ polymorphic(Base::class) {
+ subclass(BaseMessage.serializer())
+ }
+ polymorphic(ErrorClass::class) {
+ subclass(GenericError.serializer())
+ }
+ }
+ val json = Json { serializersModule = module }
+ assertJsonFormAndRestored(
+ Message.serializer(),
+ Message(BaseMessage("not found"), GenericError(404)),
+ """{"message":{"message_type":"my.app.BaseMessage","message":"not found"},"error":{"message_type":"my.app.GenericError","error_code":404}}""",
+ json
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/JsonNamesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/JsonNamesTest.kt
new file mode 100644
index 00000000..6a4e33ad
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/JsonNamesTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("ReplaceArrayOfWithLiteral") // https://youtrack.jetbrains.com/issue/KT-22578
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class JsonNamesTest : JsonTestBase() {
+
+ @Serializable
+ data class WithNames(@JsonNames("foo", "_foo") val data: String)
+
+ @Serializable
+ enum class AlternateEnumNames {
+ @JsonNames("someValue", "some_value")
+ VALUE_A,
+ VALUE_B
+ }
+
+ @Serializable
+ data class WithEnumNames(
+ val enumList: List<AlternateEnumNames>,
+ val checkCoercion: AlternateEnumNames = AlternateEnumNames.VALUE_B
+ )
+
+ @Serializable
+ data class CollisionWithAlternate(
+ @JsonNames("_foo") val data: String,
+ @JsonNames("_foo") val foo: String
+ )
+
+ private val inputString1 = """{"foo":"foo"}"""
+ private val inputString2 = """{"_foo":"foo"}"""
+
+ private fun parameterizedCoercingTest(test: (json: Json, streaming: JsonTestingMode, msg: String) -> Unit) {
+ for (coercing in listOf(true, false)) {
+ val json = Json {
+ coerceInputValues = coercing
+ useAlternativeNames = true
+ }
+ parametrizedTest { streaming ->
+ test(
+ json, streaming,
+ "Failed test with coercing=$coercing and streaming=$streaming"
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testEnumSupportsAlternativeNames() = noLegacyJs {
+ val input = """{"enumList":["VALUE_A", "someValue", "some_value", "VALUE_B"], "checkCoercion":"someValue"}"""
+ val expected = WithEnumNames(
+ listOf(
+ AlternateEnumNames.VALUE_A,
+ AlternateEnumNames.VALUE_A,
+ AlternateEnumNames.VALUE_A,
+ AlternateEnumNames.VALUE_B
+ ), AlternateEnumNames.VALUE_A
+ )
+ parameterizedCoercingTest { json, streaming, msg ->
+ assertEquals(expected, json.decodeFromString(input, streaming), msg)
+ }
+ }
+
+ @Test
+ fun topLevelEnumSupportAlternativeNames() = noLegacyJs {
+ parameterizedCoercingTest { json, streaming, msg ->
+ assertEquals(AlternateEnumNames.VALUE_A, json.decodeFromString("\"someValue\"", streaming), msg)
+ }
+ }
+
+ @Test
+ fun testParsesAllAlternativeNames() = noLegacyJs {
+ for (input in listOf(inputString1, inputString2)) {
+ parameterizedCoercingTest { json, streaming, _ ->
+ val data = json.decodeFromString(WithNames.serializer(), input, jsonTestingMode = streaming)
+ assertEquals("foo", data.data, "Failed to parse input '$input' with streaming=$streaming")
+ }
+ }
+ }
+
+ @Test
+ fun testThrowsAnErrorOnDuplicateNames() = noLegacyJs {
+ val serializer = CollisionWithAlternate.serializer()
+ parameterizedCoercingTest { json, streaming, _ ->
+ assertFailsWithMessage<SerializationException>(
+ """The suggested name '_foo' for property foo is already one of the names for property data""",
+ "Class ${serializer.descriptor.serialName} did not fail with streaming=$streaming"
+ ) {
+ json.decodeFromString(
+ serializer, inputString2,
+ jsonTestingMode = streaming
+ )
+ }
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/LocalClassesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/LocalClassesTest.kt
new file mode 100644
index 00000000..b9ba6ff4
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/LocalClassesTest.kt
@@ -0,0 +1,93 @@
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.PrimitiveKind
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.descriptors.buildSerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.test.jvmOnly
+import kotlinx.serialization.test.noLegacyJs
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class LocalClassesTest {
+ object ObjectCustomSerializer: KSerializer<Any?> {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("tmp", PrimitiveKind.INT)
+ override fun serialize(encoder: Encoder, value: Any?) {
+ encoder.encodeNull()
+ }
+
+ override fun deserialize(decoder: Decoder): Any? {
+ return decoder.decodeNull()
+ }
+ }
+
+ class ClassCustomSerializer: KSerializer<Any?> {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("tmp", PrimitiveKind.INT)
+ override fun serialize(encoder: Encoder, value: Any?) {
+ encoder.encodeNull()
+ }
+
+ override fun deserialize(decoder: Decoder): Any? {
+ return decoder.decodeNull()
+ }
+ }
+
+ @Test
+ fun testGeneratedSerializer() {
+ @Serializable
+ data class Local(val i: Int)
+
+ val origin = Local(42)
+
+ noLegacyJs {
+ val decoded: Local = Json.decodeFromString(Json.encodeToString(origin))
+ assertEquals(origin, decoded)
+ }
+ }
+
+ @Test
+ fun testInLambda() {
+ 42.let {
+ @Serializable
+ data class Local(val i: Int)
+
+ val origin = Local(it)
+
+ noLegacyJs {
+ val decoded: Local = Json.decodeFromString(Json.encodeToString(origin))
+ assertEquals(origin, decoded)
+ }
+ }
+ }
+
+ @Test
+ fun testObjectCustomSerializer() {
+ @Serializable(with = ObjectCustomSerializer::class)
+ data class Local(val i: Int)
+
+ val origin: Local? = null
+
+ noLegacyJs {
+ val decoded: Local? = Json.decodeFromString(Json.encodeToString(origin))
+ assertEquals(origin, decoded)
+ }
+ }
+
+ @Test
+ fun testClassCustomSerializer() {
+ @Serializable(with = ClassCustomSerializer::class)
+ data class Local(val i: Int)
+
+ val origin: Local? = null
+
+ // FIXME change to `noLegacyJs` when lookup of `ClassCustomSerializer` will work on Native and JS/IR
+ jvmOnly {
+ val decoded: Local? = Json.decodeFromString(Json.encodeToString(origin))
+ assertEquals(origin, decoded)
+ }
+ }
+
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/LongAsStringTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/LongAsStringTest.kt
new file mode 100644
index 00000000..c459b6a3
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/LongAsStringTest.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+class LongAsStringTest : JsonTestBase() {
+ @Serializable
+ data class HasLong(@Serializable(LongAsStringSerializer::class) val l: Long)
+
+ @Test
+ fun canSerializeAsStringAndParseBack() = parametrizedTest { jsonTestingMode ->
+ val original = HasLong(Long.MAX_VALUE - 1)
+ val str = default.encodeToString(HasLong.serializer(), original, jsonTestingMode)
+ assertEquals("""{"l":"9223372036854775806"}""", str)
+ val restored = default.decodeFromString(HasLong.serializer(), str, jsonTestingMode)
+ assertEquals(original, restored)
+ }
+
+ @Test
+ fun canNotDeserializeInvalidString() = parametrizedTest { jsonTestingMode ->
+ val str = """{"l": "this is definitely not a long"}"""
+ assertFailsWith<NumberFormatException> { default.decodeFromString(HasLong.serializer(), str, jsonTestingMode) }
+ val str2 = """{"l": "1000000000000000000000"}""" // toooo long for Long
+ assertFailsWith<NumberFormatException> { default.decodeFromString(HasLong.serializer(), str2, jsonTestingMode) }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/ObjectSerialization.kt b/formats/json/commonTest/src/kotlinx/serialization/features/ObjectSerialization.kt
new file mode 100644
index 00000000..da5919c1
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/ObjectSerialization.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class ObjectSerializationTest : JsonTestBase() {
+
+ sealed class ApiResponse {
+ @Serializable
+ @SerialName("ApiError")
+ object Error : ApiResponse()
+
+ @Serializable
+ @SerialName("ApiResponse")
+ data class Response(val message: String) : ApiResponse()
+ }
+
+ @Serializable
+ data class ApiCarrier(@Polymorphic val response: ApiResponse)
+
+ val module = SerializersModule {
+ polymorphic(ApiResponse::class) {
+ subclass(ApiResponse.Error.serializer())
+ subclass(ApiResponse.Response.serializer())
+ }
+ }
+
+ val json = Json { serializersModule = module }
+
+ @Test
+ fun testSealedClassSerialization() {
+ val carrier1 = ApiCarrier(ApiResponse.Error)
+ val carrier2 = ApiCarrier(ApiResponse.Response("OK"))
+ assertJsonFormAndRestored(ApiCarrier.serializer(), carrier1, """{"response":{"type":"ApiError"}}""", json)
+ assertJsonFormAndRestored(
+ ApiCarrier.serializer(),
+ carrier2,
+ """{"response":{"type":"ApiResponse","message":"OK"}}""",
+ json
+ )
+ }
+
+ @Test
+ fun testUnknownKeys() {
+ val string = """{"metadata":"foo"}"""
+ assertFailsWithMessage<SerializationException>("ignoreUnknownKeys") {
+ Json.decodeFromString(
+ ApiResponse.Error.serializer(),
+ string
+ )
+ }
+ val json = Json { ignoreUnknownKeys = true }
+ assertEquals(ApiResponse.Error, json.decodeFromString(ApiResponse.Error.serializer(), string))
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/PartiallyCustomSerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/PartiallyCustomSerializerTest.kt
new file mode 100644
index 00000000..71379d2d
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/PartiallyCustomSerializerTest.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.Json
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+@Serializable
+data class WithNull(@SerialName("value") val nullable: String? = null) {
+ @Serializer(forClass = WithNull::class)
+ companion object : KSerializer<WithNull> {
+ override fun serialize(encoder: Encoder, value: WithNull) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ if (value.nullable != null) elemOutput.encodeStringElement(descriptor, 0, value.nullable)
+ elemOutput.endStructure(descriptor)
+ }
+ }
+}
+
+class PartiallyCustomSerializerTest {
+ @Test
+ fun partiallyCustom() {
+ assertEquals("""{"value":"foo"}""", Json.encodeToString(WithNull.serializer(), WithNull("foo")))
+ assertEquals("""{}""", Json.encodeToString(WithNull.serializer(), WithNull()))
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/PolymorphicOnClassesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/PolymorphicOnClassesTest.kt
new file mode 100644
index 00000000..8e859ee2
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/PolymorphicOnClassesTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.reflect.*
+import kotlin.test.*
+
+class PolymorphicOnClassesTest {
+
+ // this has implicit @Polymorphic
+ interface IMessage {
+ val body: String
+ }
+
+ // and this class too has implicit @Polymorphic
+ @Serializable
+ abstract class Message : IMessage {
+ abstract override val body: String
+ }
+
+ @Polymorphic
+ @Serializable
+ @SerialName("SimpleMessage") // to cut out package prefix
+ open class SimpleMessage : Message() {
+ override var body: String = "Simple"
+ }
+
+ @Serializable
+ @SerialName("DoubleSimpleMessage")
+ class DoubleSimpleMessage(val body2: String) : SimpleMessage()
+
+ @Serializable
+ @SerialName("MessageWithId")
+ open class MessageWithId(val id: Int, override val body: String) : Message()
+
+ @Serializable
+ class Holder(
+ val iMessage: IMessage,
+ val iMessageList: List<IMessage>,
+ val message: Message,
+ val msgSet: Set<Message>,
+ val simple: SimpleMessage,
+ // all above should be polymorphic
+ val withId: MessageWithId // but this not
+ )
+
+
+ private fun genTestData(): Holder {
+ var cnt = -1
+ fun gen(): MessageWithId {
+ cnt++
+ return MessageWithId(cnt, "Message #$cnt")
+ }
+
+ return Holder(gen(), listOf(gen(), gen()), gen(), setOf(SimpleMessage()), DoubleSimpleMessage("DoubleSimple"), gen())
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private val testModule = SerializersModule {
+ listOf(Message::class, IMessage::class, SimpleMessage::class).forEach { clz ->
+ polymorphic(clz as KClass<IMessage>) {
+ subclass(SimpleMessage.serializer())
+ subclass(DoubleSimpleMessage.serializer())
+ subclass(MessageWithId.serializer())
+ }
+ }
+ }
+
+ @Test
+ fun testEnablesImplicitlyOnInterfacesAndAbstractClasses() {
+ val json = Json {
+ prettyPrint = false
+ useArrayPolymorphism = true
+ serializersModule = testModule
+ encodeDefaults = true
+ }
+ val data = genTestData()
+ assertEquals(
+ """{"iMessage":["MessageWithId",{"id":0,"body":"Message #0"}],""" +
+ """"iMessageList":[["MessageWithId",{"id":1,"body":"Message #1"}],""" +
+ """["MessageWithId",{"id":2,"body":"Message #2"}]],"message":["MessageWithId",{"id":3,"body":"Message #3"}],""" +
+ """"msgSet":[["SimpleMessage",{"body":"Simple"}]],"simple":["DoubleSimpleMessage",{"body":"Simple",""" +
+ """"body2":"DoubleSimple"}],"withId":{"id":4,"body":"Message #4"}}""",
+ json.encodeToString(Holder.serializer(), data)
+ )
+ }
+
+ @Test
+ fun testDescriptor() {
+ val polyDesc = Holder.serializer().descriptor.elementDescriptors.first()
+ assertEquals(PolymorphicSerializer(IMessage::class).descriptor, polyDesc)
+ assertEquals(2, polyDesc.elementsCount)
+ assertEquals(PrimitiveKind.STRING, polyDesc.getElementDescriptor(0).kind)
+ }
+
+ private fun SerialDescriptor.inContext(module: SerializersModule): List<SerialDescriptor> = when (kind) {
+ PolymorphicKind.OPEN -> module.getPolymorphicDescriptors(this)
+ else -> error("Expected this function to be called on OPEN descriptor")
+ }
+
+ @Test
+ fun testResolvePolymorphicDescriptor() {
+ val polyDesc = Holder.serializer().descriptor.elementDescriptors.first() // iMessage: IMessage
+
+ assertEquals(PolymorphicKind.OPEN, polyDesc.kind)
+
+ val inheritors = polyDesc.inContext(testModule)
+ val names = listOf("SimpleMessage", "DoubleSimpleMessage", "MessageWithId").toSet()
+ assertEquals(names, inheritors.map(SerialDescriptor::serialName).toSet(), "Expected correct inheritor names")
+ assertTrue(inheritors.all { it.kind == StructureKind.CLASS }, "Expected all inheritors to be CLASS")
+ }
+
+ @Test
+ fun testDocSampleWithAllDistinct() {
+ fun allDistinctNames(descriptor: SerialDescriptor, module: SerializersModule) = when (descriptor.kind) {
+ is PolymorphicKind.OPEN -> module.getPolymorphicDescriptors(descriptor)
+ .map { it.elementNames.toList() }.flatten().toSet()
+ is SerialKind.CONTEXTUAL -> module.getContextualDescriptor(descriptor)?.elementNames?.toList().orEmpty().toSet()
+ else -> descriptor.elementNames.toSet()
+ }
+
+ val polyDesc = Holder.serializer().descriptor.elementDescriptors.first() // iMessage: IMessage
+ assertEquals(setOf("id", "body", "body2"), allDistinctNames(polyDesc, testModule))
+ assertEquals(setOf("id", "body"), allDistinctNames(MessageWithId.serializer().descriptor, testModule))
+ }
+
+ @Test
+ fun testSerializerLookupForInterface() {
+ // On JVM and JS IR it can be supported via reflection/runtime hacks
+ // on Native, unfortunately, only with intrinsics.
+ if (currentPlatform == Platform.NATIVE || currentPlatform == Platform.JS_LEGACY) return
+ val msgSer = serializer<IMessage>()
+ assertEquals(IMessage::class, (msgSer as AbstractPolymorphicSerializer).baseClass)
+ }
+
+ @Test
+ fun testSerializerLookupForAbstractClass() {
+ val absSer = serializer<Message>()
+ assertEquals(Message::class, (absSer as AbstractPolymorphicSerializer).baseClass)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt
new file mode 100644
index 00000000..d05403b8
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class PolymorphismTest : JsonTestBase() {
+
+ @Serializable
+ data class Wrapper(
+ @Id(1) @Polymorphic val polyBase1: PolyBase,
+ @Id(2) @Polymorphic val polyBase2: PolyBase
+ )
+
+ private val module: SerializersModule = BaseAndDerivedModule + SerializersModule {
+ polymorphic(
+ PolyDerived::class,
+ PolyDerived.serializer()
+ )
+ }
+
+ private val json = Json { useArrayPolymorphism = true; serializersModule = module }
+
+ @Test
+ fun testInheritanceJson() = parametrizedTest { jsonTestingMode ->
+ val obj = Wrapper(
+ PolyBase(2),
+ PolyDerived("b")
+ )
+ val bytes = json.encodeToString(Wrapper.serializer(), obj, jsonTestingMode)
+ assertEquals(
+ """{"polyBase1":["kotlinx.serialization.PolyBase",{"id":2}],""" +
+ """"polyBase2":["kotlinx.serialization.PolyDerived",{"id":1,"s":"b"}]}""", bytes
+ )
+ }
+
+ @Test
+ fun testSerializeWithExplicitPolymorphicSerializer() = parametrizedTest { jsonTestingMode ->
+ val obj = PolyDerived("b")
+ val s = json.encodeToString(PolymorphicSerializer(PolyDerived::class), obj, jsonTestingMode)
+ assertEquals("""["kotlinx.serialization.PolyDerived",{"id":1,"s":"b"}]""", s)
+ }
+
+ object PolyDefaultDeserializer : JsonTransformingSerializer<PolyDefault>(PolyDefault.serializer()) {
+ override fun transformDeserialize(element: JsonElement): JsonElement = buildJsonObject {
+ put("json", JsonObject(element.jsonObject.filterKeys { it != "type" }))
+ put("id", 42)
+ }
+ }
+
+ object EvenDefaultSerializer : SerializationStrategy<PolyBase> {
+ override val descriptor = buildClassSerialDescriptor("even") {
+ element<String>("parity")
+ }
+
+ override fun serialize(encoder: Encoder, value: PolyBase) {
+ encoder.encodeStructure(descriptor) {
+ encodeStringElement(descriptor, 0, "even")
+ }
+ }
+ }
+
+ object OddDefaultSerializer : SerializationStrategy<PolyBase> {
+ override val descriptor = buildClassSerialDescriptor("odd") {
+ element<String>("parity")
+ }
+
+ override fun serialize(encoder: Encoder, value: PolyBase) {
+ encoder.encodeStructure(descriptor) {
+ encodeStringElement(descriptor, 0, "odd")
+ }
+ }
+ }
+
+ @Test
+ fun testDefaultDeserializer() = parametrizedTest { jsonTestingMode ->
+ val withDefault = module + SerializersModule {
+ polymorphicDefaultDeserializer(PolyBase::class) { name ->
+ if (name == "foo") {
+ PolyDefaultDeserializer
+ } else {
+ null
+ }
+ }
+ }
+
+ val adjustedJson = Json { serializersModule = withDefault }
+ val string = """
+ {"polyBase1":{"type":"kotlinx.serialization.PolyBase","id":239},
+ "polyBase2":{"type":"foo","key":42}}""".trimIndent()
+ val result = adjustedJson.decodeFromString(Wrapper.serializer(), string, jsonTestingMode)
+ assertEquals(Wrapper(PolyBase(239), PolyDefault(JsonObject(mapOf("key" to JsonPrimitive(42))))), result)
+
+ val replaced = string.replace("foo", "bar")
+ assertFailsWithMessage<SerializationException>("not found") { adjustedJson.decodeFromString(Wrapper.serializer(), replaced, jsonTestingMode) }
+ }
+
+ @Test
+ fun testDefaultDeserializerForMissingDiscriminator() = parametrizedTest { jsonTestingMode ->
+ val json = Json {
+ serializersModule = module + SerializersModule {
+ polymorphicDefaultDeserializer(PolyBase::class) { name ->
+ if (name == null) {
+ PolyDefaultDeserializer
+ } else {
+ null
+ }
+ }
+ }
+ }
+ val string = """
+ {"polyBase1":{"type":"kotlinx.serialization.PolyBase","id":239},
+ "polyBase2":{"key":42}}""".trimIndent()
+ val result = json.decodeFromString(Wrapper.serializer(), string, jsonTestingMode)
+ assertEquals(Wrapper(PolyBase(239), PolyDefault(JsonObject(mapOf("key" to JsonPrimitive(42))))), result)
+ }
+
+ @Test
+ fun testDefaultSerializer() = parametrizedTest { jsonTestingMode ->
+ val json = Json {
+ serializersModule = module + SerializersModule {
+ polymorphicDefaultSerializer(PolyBase::class) { value ->
+ if (value.id % 2 == 0) {
+ EvenDefaultSerializer
+ } else {
+ OddDefaultSerializer
+ }
+ }
+ }
+ }
+ val obj = Wrapper(
+ PolyDefaultWithId(0),
+ PolyDefaultWithId(1)
+ )
+ val s = json.encodeToString(Wrapper.serializer(), obj, jsonTestingMode)
+ assertEquals("""{"polyBase1":{"type":"even","parity":"even"},"polyBase2":{"type":"odd","parity":"odd"}}""", s)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/PolymorphismWithAnyTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/PolymorphismWithAnyTest.kt
new file mode 100644
index 00000000..9ce0ea9b
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/PolymorphismWithAnyTest.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.modules.plus
+import kotlinx.serialization.test.assertStringFormAndRestored
+import kotlinx.serialization.test.isJs
+import kotlin.test.*
+
+class PolymorphismWithAnyTest {
+
+ @Serializable
+ data class MyPolyData(val data: Map<String, @Polymorphic Any>)
+
+ @Serializable
+ data class MyPolyDataWithPolyBase(
+ val data: Map<String, @Polymorphic Any>,
+ @Polymorphic val polyBase: PolyBase
+ )
+
+ // KClass.toString() on JS prints simple name, not FQ one
+ @Suppress("NAME_SHADOWING")
+ private fun checkNotRegisteredMessage(className: String, scopeName: String, exception: SerializationException) {
+ val className = className.substringAfterLast('.')
+ val scopeName = scopeName.substringAfterLast('.')
+ val expectedText =
+ "Class '$className' is not registered for polymorphic serialization in the scope of '$scopeName'"
+ assertTrue(exception.message!!.startsWith(expectedText),
+ "Found $exception, but expected to start with: $expectedText")
+ }
+
+ @Test
+ fun testFailWithoutModulesWithCustomClass() {
+ checkNotRegisteredMessage(
+ "kotlinx.serialization.IntData", "kotlin.Any",
+ assertFailsWith<SerializationException>("not registered") {
+ Json.encodeToString(
+ MyPolyData.serializer(),
+ MyPolyData(mapOf("a" to IntData(42)))
+ )
+ }
+ )
+ }
+
+ @Test
+ fun testWithModules() {
+ val json = Json {
+ serializersModule = SerializersModule { polymorphic(Any::class) { subclass(IntData.serializer()) } }
+ }
+ assertStringFormAndRestored(
+ expected = """{"data":{"a":{"type":"kotlinx.serialization.IntData","intV":42}}}""",
+ original = MyPolyData(mapOf("a" to IntData(42))),
+ serializer = MyPolyData.serializer(),
+ format = json
+ )
+ }
+
+ /**
+ * This test should fail because PolyDerived registered in the scope of PolyBase, not kotlin.Any
+ */
+ @Test
+ fun testFailWithModulesNotInAnyScope() {
+ val json = Json { serializersModule = BaseAndDerivedModule }
+ checkNotRegisteredMessage(
+ "kotlinx.serialization.PolyDerived", "kotlin.Any",
+ assertFailsWith<SerializationException> {
+ json.encodeToString(
+ MyPolyData.serializer(),
+ MyPolyData(mapOf("a" to PolyDerived("foo")))
+ )
+ }
+ )
+ }
+
+ private val baseAndDerivedModuleAtAny = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(PolyDerived.serializer())
+ }
+ }
+
+
+ @Test
+ fun testRebindModules() {
+ val json = Json { serializersModule = baseAndDerivedModuleAtAny }
+ assertStringFormAndRestored(
+ expected = """{"data":{"a":{"type":"kotlinx.serialization.PolyDerived","id":1,"s":"foo"}}}""",
+ original = MyPolyData(mapOf("a" to PolyDerived("foo"))),
+ serializer = MyPolyData.serializer(),
+ format = json
+ )
+ }
+
+ /**
+ * This test should fail because PolyDerived registered in the scope of kotlin.Any, not PolyBase
+ */
+ @Test
+ fun testFailWithModulesNotInParticularScope() {
+ val json = Json { serializersModule = baseAndDerivedModuleAtAny }
+ checkNotRegisteredMessage(
+ "kotlinx.serialization.PolyDerived", "kotlinx.serialization.PolyBase",
+ assertFailsWith {
+ json.encodeToString(
+ MyPolyDataWithPolyBase.serializer(),
+ MyPolyDataWithPolyBase(
+ mapOf("a" to PolyDerived("foo")),
+ PolyDerived("foo")
+ )
+ )
+ }
+ )
+ }
+
+ @Test
+ fun testBindModules() {
+ val json = Json { serializersModule = (baseAndDerivedModuleAtAny + BaseAndDerivedModule) }
+ assertStringFormAndRestored(
+ expected = """{"data":{"a":{"type":"kotlinx.serialization.PolyDerived","id":1,"s":"foo"}},
+ |"polyBase":{"type":"kotlinx.serialization.PolyDerived","id":1,"s":"foo"}}""".trimMargin().lines().joinToString(
+ ""
+ ),
+ original = MyPolyDataWithPolyBase(
+ mapOf("a" to PolyDerived("foo")),
+ PolyDerived("foo")
+ ),
+ serializer = MyPolyDataWithPolyBase.serializer(),
+ format = json
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/PrimitiveArraySerializersTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/PrimitiveArraySerializersTest.kt
new file mode 100644
index 00000000..2397c177
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/PrimitiveArraySerializersTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class PrimitiveArraySerializersTest : JsonTestBase() {
+
+ @Serializable
+ data class A(
+ val arr: ByteArray,
+ val arr2: IntArray = intArrayOf(1, 2),
+ val arr3: BooleanArray = booleanArrayOf(true, false),
+ var arr4: CharArray = charArrayOf('a', 'b', 'c'),
+ val arr5: DoubleArray = doubleArrayOf(Double.NaN, 0.1, -0.25),
+ val arr6: ShortArray = shortArrayOf(1, 2, 3),
+ val arr7: LongArray = longArrayOf(1, 2, 3),
+ val arr8: FloatArray = floatArrayOf(1.25f, 2.25f, 3.25f)
+ ) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is A) return false
+
+ if (!arr.contentEquals(other.arr)) return false
+ if (!arr2.contentEquals(other.arr2)) return false
+ if (!arr3.contentEquals(other.arr3)) return false
+ if (!arr4.contentEquals(other.arr4)) return false
+ if (!arr5.contentEquals(other.arr5)) return false
+ if (!arr6.contentEquals(other.arr6)) return false
+ if (!arr7.contentEquals(other.arr7)) return false
+ if (!arr8.contentEquals(other.arr8)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = arr.contentHashCode()
+ result = 31 * result + arr2.contentHashCode()
+ result = 31 * result + arr3.contentHashCode()
+ result = 31 * result + arr4.contentHashCode()
+ result = 31 * result + arr5.contentHashCode()
+ result = 31 * result + arr6.contentHashCode()
+ result = 31 * result + arr7.contentHashCode()
+ result = 31 * result + arr8.contentHashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "A(arr=${arr.contentToString()}, arr2=${arr2.contentToString()}, arr3=${arr3.contentToString()}, arr4=${arr4.contentToString()}, arr5=${arr5.contentToString()}, arr6=${arr6.contentToString()}, arr7=${arr7.contentToString()}, arr8=${arr8.contentToString()})"
+ }
+ }
+
+ @Test
+ fun testCanBeSerialized() = assertStringFormAndRestored(
+ """{"arr":[1,2,3],"arr2":[1,2],"arr3":[true,false],"arr4":["a","b","c"],"arr5":[NaN,0.1,-0.25],"arr6":[1,2,3],"arr7":[1,2,3],"arr8":[1.25,2.25,3.25]}""",
+ A(byteArrayOf(1, 2, 3)),
+ A.serializer(),
+ lenient
+ )
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/PropertyInitializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/PropertyInitializerTest.kt
new file mode 100644
index 00000000..f33069b8
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/PropertyInitializerTest.kt
@@ -0,0 +1,105 @@
+@file:Suppress("MayBeConstant")
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.Json
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+internal val globalVar: Int = 4
+
+internal fun globalFun(): Int {
+ return 7
+}
+
+internal const val PROPERTY_INITIALIZER_JSON = """{
+ "valProperty": 1,
+ "varProperty": 2,
+ "literalConst": 3,
+ "globalVarRef": 4,
+ "computed": 5,
+ "doubleRef": 6,
+ "globalFun": 7,
+ "globalFunExpr": 8,
+ "itExpr": 9,
+ "transientRefFromProp": 10,
+ "bodyProp": 11,
+ "dependBodyProp": 12,
+ "getterDepend": 13
+}"""
+
+@Suppress("MemberVisibilityCanBePrivate", "unused", "ComplexRedundantLet")
+class PropertyInitializerTest {
+ @Serializable
+ data class InternalClass(
+ val valProperty: Int,
+ var varProperty: Int,
+ val literalConst: Int = 3,
+ val globalVarRef: Int = globalVar,
+ val computed: Int = valProperty + varProperty + 2,
+ val doubleRef: Int = literalConst + literalConst,
+ var globalFun: Int = globalFun(),
+ var globalFunExpr: Int = globalFun() + 1,
+ val itExpr: Int = literalConst.let { it + 6 },
+ @Transient val constTransient: Int = 6,
+ @Transient val serializedRefTransient: Int = varProperty + 1,
+ @Transient val refTransient: Int = serializedRefTransient,
+ val transientRefFromProp: Int = constTransient + 4,
+ ) {
+ val valGetter: Int get() { return 5 }
+ var bodyProp: Int = 11
+ var dependBodyProp: Int = bodyProp + 1
+ var getterDepend: Int = valGetter + 8
+ }
+
+ private val format = Json { encodeDefaults = true; prettyPrint = true }
+
+ data class ExternalClass(
+ val valProperty: Int,
+ var varProperty: Int,
+ val literalConst: Int = 3,
+ val globalVarRef: Int = globalVar,
+ val computed: Int = valProperty + varProperty + 2,
+ val doubleRef: Int = literalConst + literalConst,
+ var globalFun: Int = globalFun(),
+ var globalFunExpr: Int = globalFun() + 1,
+ val itExpr: Int = literalConst.let { it + 6 },
+ @Transient val constTransient: Int = 6,
+ @Transient val serializedRefTransient: Int = varProperty + 1,
+ @Transient val refTransient: Int = serializedRefTransient,
+ val transientRefFromProp: Int = constTransient + 4,
+ ) {
+ val valGetter: Int get() { return 5 }
+ var bodyProp: Int = 11
+ var dependBodyProp: Int = bodyProp + 1
+ var getterDepend: Int = valGetter + 8
+ }
+
+ @Serializer(ExternalClass::class)
+ object ExternalSerializer
+
+ @Test
+ fun testInternalSerializeDefault() {
+ val encoded = format.encodeToString(InternalClass(1, 2))
+ assertEquals(PROPERTY_INITIALIZER_JSON, encoded)
+ }
+
+ @Test
+ fun testInternalDeserializeDefault() {
+ val decoded = format.decodeFromString<InternalClass>("""{"valProperty": 5, "varProperty": 6}""")
+ assertEquals(InternalClass(5, 6), decoded)
+ }
+
+ @Test
+ fun testExternalSerializeDefault() {
+ val encoded = format.encodeToString(ExternalSerializer, ExternalClass(1, 2))
+ assertEquals(PROPERTY_INITIALIZER_JSON, encoded)
+ }
+
+ @Test
+ fun testExternalDeserializeDefault() {
+ val decoded = format.decodeFromString(ExternalSerializer,"""{"valProperty": 5, "varProperty": 6}""")
+ assertEquals(ExternalClass(5, 6), decoded)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/SealedClassesSerializationTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/SealedClassesSerializationTest.kt
new file mode 100644
index 00000000..8af90553
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/SealedClassesSerializationTest.kt
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.features.sealed.SealedChild
+import kotlinx.serialization.features.sealed.SealedParent
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class SealedClassesSerializationTest : JsonTestBase() {
+ @Serializable
+ sealed class SealedProtocol {
+ @Serializable
+ @SerialName("SealedProtocol.StringMessage")
+ data class StringMessage(val description: String, val message: String) : SealedProtocol()
+
+ @Serializable
+ @SerialName("SealedProtocol.IntMessage")
+ data class IntMessage(val description: String, val message: Int) : SealedProtocol()
+
+ @Serializable
+ @SerialName("SealedProtocol.ErrorMessage")
+ data class ErrorMessage(val error: String) : SealedProtocol()
+
+ @SerialName("EOF")
+ @Serializable
+ object EOF : SealedProtocol()
+ }
+
+ @Serializable
+ sealed class ProtocolWithAbstractClass {
+
+ @Serializable
+ @SerialName("ProtocolWithAbstractClass.Message")
+ abstract class Message : ProtocolWithAbstractClass() {
+ @Serializable
+ @SerialName("ProtocolWithAbstractClass.Message.StringMessage")
+ data class StringMessage(val description: String, val message: String) : Message()
+
+ @Serializable
+ @SerialName("ProtocolWithAbstractClass.Message.IntMessage")
+ data class IntMessage(val description: String, val message: Int) : Message()
+ }
+
+ @Serializable
+ @SerialName("ProtocolWithAbstractClass.ErrorMessage")
+ data class ErrorMessage(val error: String) : ProtocolWithAbstractClass()
+
+ @SerialName("EOF")
+ @Serializable
+ object EOF : ProtocolWithAbstractClass()
+ }
+
+ @Serializable
+ sealed class ProtocolWithSealedClass {
+
+ @Serializable
+ @SerialName("ProtocolWithSealedClass.Message")
+ sealed class Message : ProtocolWithSealedClass() {
+ @Serializable
+ @SerialName("ProtocolWithSealedClass.Message.StringMessage")
+ data class StringMessage(val description: String, val message: String) : Message()
+
+ @Serializable
+ @SerialName("ProtocolWithSealedClass.Message.IntMessage")
+ data class IntMessage(val description: String, val message: Int) : Message()
+ }
+
+ @Serializable
+ @SerialName("ProtocolWithSealedClass.ErrorMessage")
+ data class ErrorMessage(val error: String) : ProtocolWithSealedClass()
+
+ @SerialName("EOF")
+ @Serializable
+ object EOF : ProtocolWithSealedClass()
+ }
+
+ @Serializable
+ sealed class ProtocolWithGenericClass {
+
+ @Serializable
+ @SerialName("ProtocolWithGenericClass.Message")
+ data class Message<T>(val description: String, val message: T) : ProtocolWithGenericClass()
+
+ @Serializable
+ @SerialName("ProtocolWithGenericClass.ErrorMessage")
+ data class ErrorMessage(val error: String) : ProtocolWithGenericClass()
+
+ @SerialName("EOF")
+ @Serializable
+ object EOF : ProtocolWithGenericClass()
+ }
+
+ private val ManualSerializer: KSerializer<SimpleSealed> = SealedClassSerializer(
+ "SimpleSealed",
+ SimpleSealed::class,
+ arrayOf(SimpleSealed.SubSealedA::class, SimpleSealed.SubSealedB::class),
+ arrayOf(SimpleSealed.SubSealedA.serializer(), SimpleSealed.SubSealedB.serializer())
+ )
+
+ @Serializable
+ data class SealedHolder(val s: SimpleSealed)
+
+ @Serializable
+ data class SealedBoxHolder(val b: Box<SimpleSealed>)
+
+ private val arrayJson = Json { useArrayPolymorphism = true }
+ private val json = Json
+
+ @Test
+ fun manualSerializer() {
+ val message = json.encodeToString(
+ ManualSerializer,
+ SimpleSealed.SubSealedB(42)
+ )
+ assertEquals("{\"type\":\"kotlinx.serialization.SimpleSealed.SubSealedB\",\"i\":42}", message)
+ }
+
+ @Test
+ fun onTopLevel() {
+ val arrayMessage = arrayJson.encodeToString(
+ SimpleSealed.serializer(),
+ SimpleSealed.SubSealedB(42)
+ )
+ val message = json.encodeToString(
+ SimpleSealed.serializer(),
+ SimpleSealed.SubSealedB(42)
+ )
+ assertEquals("{\"type\":\"kotlinx.serialization.SimpleSealed.SubSealedB\",\"i\":42}", message)
+ assertEquals("[\"kotlinx.serialization.SimpleSealed.SubSealedB\",{\"i\":42}]", arrayMessage)
+ }
+
+ @Test
+ fun insideClass() {
+ assertJsonFormAndRestored(
+ SealedHolder.serializer(),
+ SealedHolder(SimpleSealed.SubSealedA("foo")),
+ """{"s":{"type":"kotlinx.serialization.SimpleSealed.SubSealedA","s":"foo"}}""",
+ json
+ )
+ }
+
+ @Test
+ fun insideGeneric() {
+ assertJsonFormAndRestored(
+ Box.serializer(SimpleSealed.serializer()),
+ Box<SimpleSealed>(SimpleSealed.SubSealedA("foo")),
+ """{"boxed":{"type":"kotlinx.serialization.SimpleSealed.SubSealedA","s":"foo"}}""",
+ json
+ )
+ assertJsonFormAndRestored(
+ SealedBoxHolder.serializer(),
+ SealedBoxHolder(Box(SimpleSealed.SubSealedA("foo"))),
+ """{"b":{"boxed":{"type":"kotlinx.serialization.SimpleSealed.SubSealedA","s":"foo"}}}""",
+ json
+ )
+ }
+
+ @Test
+ fun complexProtocol() {
+ val messages = listOf<SealedProtocol>(
+ SealedProtocol.StringMessage("string message", "foo"),
+ SealedProtocol.IntMessage("int message", 42),
+ SealedProtocol.ErrorMessage("requesting termination"),
+ SealedProtocol.EOF
+ )
+ val expected =
+ """[{"type":"SealedProtocol.StringMessage","description":"string message","message":"foo"},{"type":"SealedProtocol.IntMessage","description":"int message","message":42},{"type":"SealedProtocol.ErrorMessage","error":"requesting termination"},{"type":"EOF"}]"""
+ assertJsonFormAndRestored(ListSerializer(SealedProtocol.serializer()), messages, expected, json)
+ }
+
+ @Test
+ fun protocolWithAbstractClass() {
+ val messages = listOf<ProtocolWithAbstractClass>(
+ ProtocolWithAbstractClass.Message.StringMessage("string message", "foo"),
+ ProtocolWithAbstractClass.Message.IntMessage("int message", 42),
+ ProtocolWithAbstractClass.ErrorMessage("requesting termination"),
+ ProtocolWithAbstractClass.EOF
+ )
+ val abstractContext = SerializersModule {
+
+ polymorphic(ProtocolWithAbstractClass::class) {
+ subclass(ProtocolWithAbstractClass.Message.IntMessage.serializer())
+ subclass(ProtocolWithAbstractClass.Message.StringMessage.serializer())
+ }
+
+ polymorphic(ProtocolWithAbstractClass.Message::class) {
+ subclass(ProtocolWithAbstractClass.Message.IntMessage.serializer())
+ subclass(ProtocolWithAbstractClass.Message.StringMessage.serializer())
+ }
+ }
+ val json = Json {
+ serializersModule = abstractContext
+ }
+ val expected =
+ """[{"type":"ProtocolWithAbstractClass.Message.StringMessage","description":"string message","message":"foo"},{"type":"ProtocolWithAbstractClass.Message.IntMessage","description":"int message","message":42},{"type":"ProtocolWithAbstractClass.ErrorMessage","error":"requesting termination"},{"type":"EOF"}]"""
+ assertJsonFormAndRestored(ListSerializer(ProtocolWithAbstractClass.serializer()), messages, expected, json)
+ }
+
+ @Test
+ fun protocolWithSealedClass() {
+ val messages = listOf<ProtocolWithSealedClass>(
+ ProtocolWithSealedClass.Message.StringMessage("string message", "foo"),
+ ProtocolWithSealedClass.Message.IntMessage("int message", 42),
+ ProtocolWithSealedClass.ErrorMessage("requesting termination"),
+ ProtocolWithSealedClass.EOF
+ )
+ val expected =
+ """[{"type":"ProtocolWithSealedClass.Message.StringMessage","description":"string message","message":"foo"},{"type":"ProtocolWithSealedClass.Message.IntMessage","description":"int message","message":42},{"type":"ProtocolWithSealedClass.ErrorMessage","error":"requesting termination"},{"type":"EOF"}]"""
+ assertJsonFormAndRestored(ListSerializer(ProtocolWithSealedClass.serializer()), messages, expected, json)
+ }
+
+ @Test
+ fun partOfProtocolWithSealedClass() {
+ val messages = listOf<ProtocolWithSealedClass.Message>(
+ ProtocolWithSealedClass.Message.StringMessage("string message", "foo"),
+ ProtocolWithSealedClass.Message.IntMessage("int message", 42)
+ )
+ val expected =
+ """[{"type":"ProtocolWithSealedClass.Message.StringMessage","description":"string message","message":"foo"},{"type":"ProtocolWithSealedClass.Message.IntMessage","description":"int message","message":42}]"""
+
+ assertJsonFormAndRestored(ListSerializer(ProtocolWithSealedClass.serializer()), messages, expected, json)
+ assertJsonFormAndRestored(ListSerializer(ProtocolWithSealedClass.Message.serializer()), messages, expected, json)
+ }
+
+ @Test
+ fun protocolWithGenericClass() {
+ val messages = listOf<ProtocolWithGenericClass>(
+ ProtocolWithGenericClass.Message<String>("string message", "foo"),
+ ProtocolWithGenericClass.Message<Int>("int message", 42),
+ ProtocolWithGenericClass.ErrorMessage("requesting termination"),
+ ProtocolWithGenericClass.EOF
+ )
+ val expected =
+ """[["ProtocolWithGenericClass.Message",{"description":"string message","message":["kotlin.String","foo"]}],["ProtocolWithGenericClass.Message",{"description":"int message","message":["kotlin.Int",42]}],["ProtocolWithGenericClass.ErrorMessage",{"error":"requesting termination"}],["EOF",{}]]"""
+ val json = Json {
+ useArrayPolymorphism = true
+ serializersModule = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(Int::class)
+ subclass(String::class)
+ }
+ }
+ }
+ assertJsonFormAndRestored(ListSerializer(ProtocolWithGenericClass.serializer()), messages, expected, json)
+ }
+
+ @Test
+ fun testSerializerLookupForSealedClass() {
+ val resSer = serializer<SealedProtocol>()
+ assertEquals(SealedProtocol::class, (resSer as AbstractPolymorphicSerializer).baseClass)
+ }
+
+ @Test
+ fun testClassesFromDifferentFiles() {
+ assertJsonFormAndRestored(SealedParent.serializer(), SealedChild(5), """{"type":"first child","i":1,"j":5}""")
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/SealedPolymorphismTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/SealedPolymorphismTest.kt
new file mode 100644
index 00000000..4cc289a9
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/SealedPolymorphismTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.assertStringFormAndRestored
+import kotlin.test.Test
+
+class SealedPolymorphismTest {
+
+ @Serializable
+ data class FooHolder(
+ val someMetadata: Int,
+ val payload: List<@Polymorphic Foo>
+ )
+
+ @Serializable
+ @SerialName("Foo")
+ sealed class Foo {
+ @Serializable
+ @SerialName("Bar")
+ data class Bar(val bar: Int) : Foo()
+ @Serializable
+ @SerialName("Baz")
+ data class Baz(val baz: String) : Foo()
+ }
+
+ val sealedModule = SerializersModule {
+ polymorphic(Foo::class) {
+ subclass(Foo.Bar.serializer())
+ subclass(Foo.Baz.serializer())
+ }
+ }
+
+ val json = Json { serializersModule = sealedModule }
+
+ @Test
+ fun testSaveSealedClassesList() {
+ assertStringFormAndRestored(
+ """{"someMetadata":42,"payload":[
+ |{"type":"Bar","bar":1},
+ |{"type":"Baz","baz":"2"}]}""".trimMargin().replace("\n", ""),
+ FooHolder(42, listOf(Foo.Bar(1), Foo.Baz("2"))),
+ FooHolder.serializer(),
+ json,
+ printResult = true
+ )
+ }
+
+ @Test
+ fun testCanSerializeSealedClassPolymorphicallyOnTopLevel() {
+ assertStringFormAndRestored(
+ """{"type":"Bar","bar":1}""",
+ Foo.Bar(1),
+ PolymorphicSerializer(Foo::class),
+ json
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/SerializableOnTypeUsageTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/SerializableOnTypeUsageTest.kt
new file mode 100644
index 00000000..e991a0db
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/SerializableOnTypeUsageTest.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.Json
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+
+@Serializable
+data class SerializableOnArguments(
+ val list1: List<@Serializable(MultiplyingIntSerializer::class) Int>,
+ val list2: List<List<@Serializable(MultiplyingIntHolderSerializer::class) IntHolder>>
+)
+
+class SerializableOnTypeUsageTest {
+ @Test
+ fun testAnnotationIsApplied() {
+ val data = SerializableOnArguments(listOf(1, 2), listOf(listOf(IntHolder(42))))
+ val str = Json.encodeToString(SerializableOnArguments.serializer(), data)
+ assertEquals("""{"list1":[2,4],"list2":[[84]]}""", str)
+ val restored = Json.decodeFromString(SerializableOnArguments.serializer(), str)
+ assertEquals(data, restored)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/SerializableWithTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/SerializableWithTest.kt
new file mode 100644
index 00000000..a9cf9638
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/SerializableWithTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.Json
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+object MultiplyingIntSerializer : KSerializer<Int> {
+ override val descriptor: SerialDescriptor
+ get() = PrimitiveSerialDescriptor("MultiplyingInt", PrimitiveKind.INT)
+
+ override fun deserialize(decoder: Decoder): Int {
+ return decoder.decodeInt() / 2
+ }
+
+ override fun serialize(encoder: Encoder, value: Int) {
+ encoder.encodeInt(value * 2)
+ }
+}
+
+object DividingIntSerializer : KSerializer<Int> {
+ override val descriptor: SerialDescriptor
+ get() = PrimitiveSerialDescriptor("DividedInt", PrimitiveKind.INT)
+
+ override fun deserialize(decoder: Decoder): Int {
+ return decoder.decodeInt() * 2
+ }
+
+ override fun serialize(encoder: Encoder, value: Int) {
+ encoder.encodeInt(value / 2)
+ }
+}
+
+@Serializable(with = DividingIntHolderSerializer::class)
+data class IntHolder(val data: Int)
+
+@Serializer(IntHolder::class)
+object MultiplyingIntHolderSerializer {
+ override fun deserialize(decoder: Decoder): IntHolder {
+ return IntHolder(decoder.decodeInt() / 2)
+ }
+
+ override fun serialize(encoder: Encoder, value: IntHolder) {
+ encoder.encodeInt(value.data * 2)
+ }
+}
+
+@Serializer(IntHolder::class)
+object DividingIntHolderSerializer {
+ override fun deserialize(decoder: Decoder): IntHolder {
+ return IntHolder(decoder.decodeInt() * 2)
+ }
+
+ override fun serialize(encoder: Encoder, value: IntHolder) {
+ encoder.encodeInt(value.data / 2)
+ }
+}
+
+@Serializable
+data class Carrier(
+ @Serializable(with = MultiplyingIntHolderSerializer::class) val a: IntHolder,
+ @Serializable(with = MultiplyingIntSerializer::class) val i: Int
+)
+
+class SerializableWithTest {
+ @Test
+ fun testOnProperties() {
+ val str = Json.encodeToString(Carrier.serializer(), Carrier(IntHolder(42), 2))
+ assertEquals("""{"a":84,"i":4}""", str)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/SkipDefaults.kt b/formats/json/commonTest/src/kotlinx/serialization/features/SkipDefaults.kt
new file mode 100644
index 00000000..ee37b4b8
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/SkipDefaults.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.EncodeDefault.Mode.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.test.noLegacyJs
+import kotlin.test.*
+
+class SkipDefaultsTest {
+ private val jsonDropDefaults = Json { encodeDefaults = false }
+ private val jsonEncodeDefaults = Json { encodeDefaults = true }
+
+ @Serializable
+ data class Data(val bar: String, val foo: Int = 42) {
+ var list: List<Int> = emptyList()
+ val listWithSomething: List<Int> = listOf(1, 2, 3)
+ }
+
+ @Serializable
+ data class DifferentModes(
+ val a: String = "a",
+ @EncodeDefault val b: String = "b",
+ @EncodeDefault(ALWAYS) val c: String = "c",
+ @EncodeDefault(NEVER) val d: String = "d"
+ )
+
+ @Test
+ fun serializeCorrectlyDefaults() {
+ val d = Data("bar")
+ assertEquals(
+ """{"bar":"bar","foo":42,"list":[],"listWithSomething":[1,2,3]}""",
+ jsonEncodeDefaults.encodeToString(Data.serializer(), d)
+ )
+ }
+
+ @Test
+ fun serializeCorrectly() {
+ val d = Data("bar", 100).apply { list = listOf(1, 2, 3) }
+ assertEquals(
+ """{"bar":"bar","foo":100,"list":[1,2,3]}""",
+ jsonDropDefaults.encodeToString(Data.serializer(), d)
+ )
+ }
+
+ @Test
+ fun serializeCorrectlyAndDropBody() {
+ val d = Data("bar", 43)
+ assertEquals("""{"bar":"bar","foo":43}""", jsonDropDefaults.encodeToString(Data.serializer(), d))
+ }
+
+ @Test
+ fun serializeCorrectlyAndDropAll() {
+ val d = Data("bar")
+ assertEquals("""{"bar":"bar"}""", jsonDropDefaults.encodeToString(Data.serializer(), d))
+ }
+
+ @Test
+ fun encodeDefaultsAnnotationWithFlag() = noLegacyJs {
+ val data = DifferentModes()
+ assertEquals("""{"a":"a","b":"b","c":"c"}""", jsonEncodeDefaults.encodeToString(data))
+ assertEquals("""{"b":"b","c":"c"}""", jsonDropDefaults.encodeToString(data))
+ }
+
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/UseSerializersTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/UseSerializersTest.kt
new file mode 100644
index 00000000..b5f332d7
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/UseSerializersTest.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:UseSerializers(MultiplyingIntHolderSerializer::class, MultiplyingIntSerializer::class)
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+@Serializable
+data class Carrier2(
+ val a: IntHolder,
+ val i: Int,
+ val nullable: Int?,
+ val nullableIntHolder: IntHolder?,
+ val nullableIntList: List<Int?> = emptyList(),
+ val nullableIntHolderNullableList: List<IntHolder?>? = null
+)
+
+class UseSerializersTest {
+ @Test
+ fun testOnFile() {
+ val str = Json { encodeDefaults = true }.encodeToString(
+ Carrier2.serializer(),
+ Carrier2(IntHolder(42), 2, 2, IntHolder(42))
+ )
+ assertEquals("""{"a":84,"i":4,"nullable":4,"nullableIntHolder":84,"nullableIntList":[],"nullableIntHolderNullableList":null}""", str)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt
new file mode 100644
index 00000000..037d7858
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:OptIn(ExperimentalUnsignedTypes::class)
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features.inline
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.test.*
+import kotlin.test.Test
+
+@Serializable(WithUnsignedSerializer::class)
+data class WithUnsigned(val u: UInt)
+
+object WithUnsignedSerializer : KSerializer<WithUnsigned> {
+ override fun serialize(encoder: Encoder, value: WithUnsigned) {
+ val ce = encoder.beginStructure(descriptor)
+ ce.encodeInlineElement(descriptor, 0).encodeInt(value.u.toInt())
+ ce.endStructure(descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): WithUnsigned {
+ val cd = decoder.beginStructure(descriptor)
+ var u: UInt = 0.toUInt()
+ loop@ while (true) {
+ u = when (val i = cd.decodeElementIndex(descriptor)) {
+ 0 -> cd.decodeInlineElement(descriptor, i).decodeInt().toUInt()
+ else -> break@loop
+ }
+ }
+ cd.endStructure(descriptor)
+ return WithUnsigned(u)
+ }
+
+ override val descriptor: SerialDescriptor = buildClassSerialDescriptor("WithUnsigned") {
+ element("u", UInt.serializer().descriptor)
+ }
+}
+
+class EncodeInlineElementTest {
+ @Test
+ fun wrapper() = noLegacyJs {
+ val w = WithUnsigned(Int.MAX_VALUE.toUInt() + 1.toUInt())
+ assertStringFormAndRestored("""{"u":2147483648}""", w, WithUnsignedSerializer, printResult = true)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesCompleteTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesCompleteTest.kt
new file mode 100644
index 00000000..363bdaf9
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesCompleteTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:Suppress("INLINE_CLASSES_NOT_SUPPORTED", "SERIALIZER_NOT_FOUND")
+
+package kotlinx.serialization.features.inline
+
+import kotlinx.serialization.*
+import kotlinx.serialization.Box
+import kotlinx.serialization.test.*
+import kotlin.jvm.*
+import kotlin.test.*
+
+@Serializable
+@JvmInline
+value class MyInt(val i: Int)
+
+@Serializable
+@JvmInline value class NullableMyInt(val i: Int?)
+
+@Serializable
+@JvmInline value class OverSerializable(val s: IntData)
+
+@Serializable
+@JvmInline value class OverSerializableNullable(val s: IntData?)
+
+@Serializable
+@JvmInline value class WithT<T>(val t: Box<T>)
+
+@Serializable
+@JvmInline value class WithTNullable<T>(val t: Box<T>?)
+
+@Serializable
+data class WithAll(
+ val myInt: MyInt, // I
+ val myIntNullable: MyInt?, // LMyInt;
+ val nullableMyInt: NullableMyInt, //Ljava/lang/Integer;
+ val nullableMyIntNullable: NullableMyInt?, // LNullableMyInt
+ val overSerializable: OverSerializable, // LIntData;
+ val overSerializableNullable: OverSerializable?, // LIntData;
+ val nullableOverSerializable: OverSerializableNullable, // LIntData;
+ val nullableOverSerializableNullable: OverSerializableNullable?, // LOverSerializableNullable;
+ val withT: WithT<Int>, // LBox;
+ val withTNullable: WithT<Int>?, // LBox
+ val withNullableTNullable: WithT<Int?>?, // LBox;
+ val withTNullableTNullable: WithTNullable<Int?>? // LWithTNullable;
+)
+
+@Serializable
+data class WithGenerics(
+ val myInt: Box<MyInt>,
+ val myIntNullable: Box<MyInt?>,
+ val nullableMyInt: Box<NullableMyInt>,
+ val nullableMyIntNullable: Box<NullableMyInt?>,
+ val overSerializable: Box<OverSerializable>,
+ val overSerializableNullable: Box<OverSerializable?>,
+ val nullableOverSerializable: Box<OverSerializableNullable>,
+ val nullableOverSerializableNullable: Box<OverSerializableNullable?>,
+ val boxInBox: Box<WithT<Int>>
+)
+
+class InlineClassesCompleteTest {
+ @Test
+ fun testAllVariantsWithoutNull() = noLegacyJs {
+ val withAll = WithAll(
+ MyInt(1),
+ MyInt(2),
+ NullableMyInt(3),
+ NullableMyInt(4),
+ OverSerializable(IntData(5)),
+ OverSerializable(IntData(6)),
+ OverSerializableNullable(IntData(7)),
+ OverSerializableNullable(IntData(8)),
+ WithT(Box(9)),
+ WithT<Int>(Box(10)),
+ WithT<Int?>(Box(11)),
+ WithTNullable<Int?>(Box(12))
+ )
+ assertSerializedAndRestored(withAll, WithAll.serializer())
+ }
+
+ @Test
+ fun testAllVariantsWithNull() = noLegacyJs {
+ assertSerializedAndRestored(
+ WithAll(
+ MyInt(1),
+ null,
+ NullableMyInt(null),
+ null,
+ OverSerializable(IntData(5)),
+ null,
+ OverSerializableNullable(null),
+ null,
+ WithT(Box(9)),
+ null,
+ WithT<Int?>(Box(null)),
+ WithTNullable<Int?>(Box(null))
+ ), WithAll.serializer()
+ )
+ }
+
+ @Test
+ fun testAllGenericVariantsWithoutNull() = noLegacyJs {
+ assertSerializedAndRestored(
+ WithGenerics(
+ Box(MyInt(1)),
+ Box(MyInt(2)),
+ Box(NullableMyInt(3)),
+ Box(NullableMyInt(4)),
+ Box(OverSerializable(IntData(5))),
+ Box(OverSerializable(IntData(6))),
+ Box(OverSerializableNullable(IntData(7))),
+ Box(OverSerializableNullable(IntData(8))),
+ Box(WithT(Box(9)))
+ ), WithGenerics.serializer()
+ )
+ }
+
+ @Test
+ fun testAllGenericVariantsWithNull() = noLegacyJs {
+ assertSerializedAndRestored(
+ WithGenerics(
+ Box(MyInt(1)),
+ Box(null),
+ Box(NullableMyInt(null)),
+ Box(null),
+ Box(OverSerializable(IntData(5))),
+ Box(null),
+ Box(OverSerializableNullable(null)),
+ Box(null),
+ Box(WithT(Box(9)))
+ ), WithGenerics.serializer()
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt
new file mode 100644
index 00000000..1eedafae
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("INLINE_CLASSES_NOT_SUPPORTED", "SERIALIZER_NOT_FOUND")
+@file:OptIn(ExperimentalUnsignedTypes::class)
+
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features.inline
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.jvm.*
+import kotlin.test.*
+
+@Serializable
+data class SimpleContainerForUInt(val i: UInt)
+
+@Serializable(MyUIntSerializer::class)
+@JvmInline
+value class MyUInt(val m: Int)
+
+@Serializer(forClass = MyUInt::class)
+object MyUIntSerializer {
+ override val descriptor = UInt.serializer().descriptor
+ override fun serialize(encoder: Encoder, value: MyUInt) {
+ encoder.encodeInline(descriptor).encodeInt(value.m)
+ }
+
+ override fun deserialize(decoder: Decoder): MyUInt {
+ return MyUInt(decoder.decodeInline(descriptor).decodeInt())
+ }
+}
+
+@Serializable
+data class SimpleContainerForMyType(val i: MyUInt)
+
+@Serializable
+@JvmInline
+value class MyList<T>(val list: List<T>)
+
+@Serializable
+data class ContainerForList<T>(val i: MyList<T>)
+
+@Serializable
+data class UnsignedInBoxedPosition(val i: List<UInt>)
+
+@Serializable
+data class MixedPositions(
+ val int: Int,
+ val intNullable: Int?,
+ val uint: UInt,
+ val uintNullable: UInt?,
+ val boxedInt: List<Int>,
+ val boxedUInt: List<UInt>,
+ val boxedNullableInt: List<Int?>,
+ val boxedNullableUInt: List<UInt?>
+)
+
+@Serializable
+@JvmInline
+value class ResourceId(val id: String)
+
+@Serializable
+@JvmInline
+value class ResourceType(val type: String)
+
+@Serializable
+@JvmInline
+value class ResourceKind(val kind: SampleEnum)
+
+@Serializable
+data class ResourceIdentifier(val id: ResourceId, val type: ResourceType, val type2: ValueWrapper)
+
+@Serializable @JvmInline
+value class ValueWrapper(val wrapped: ResourceType)
+
+class InlineClassesTest : JsonTestBase() {
+ private val precedent: UInt = Int.MAX_VALUE.toUInt() + 10.toUInt()
+
+ @Test
+ fun testTopLevel() = noLegacyJs {
+ assertJsonFormAndRestored(
+ ResourceType.serializer(),
+ ResourceType("foo"),
+ """"foo"""",
+ )
+ }
+
+ @Test
+ fun testTopLevelOverEnum() = noLegacyJs {
+ assertJsonFormAndRestored(
+ ResourceKind.serializer(),
+ ResourceKind(SampleEnum.OptionC),
+ """"OptionC"""",
+ )
+ }
+
+ @Test
+ fun testTopLevelWrapper() = noLegacyJs {
+ assertJsonFormAndRestored(
+ ValueWrapper.serializer(),
+ ValueWrapper(ResourceType("foo")),
+ """"foo"""",
+ )
+ }
+
+ @Test
+ fun testTopLevelContextual() = noLegacyJs {
+ val module = SerializersModule {
+ contextual<ResourceType>(ResourceType.serializer())
+ }
+ val json = Json(default) { serializersModule = module }
+ assertJsonFormAndRestored(
+ ContextualSerializer(ResourceType::class),
+ ResourceType("foo"),
+ """"foo"""",
+ json
+ )
+ }
+
+
+ @Test
+ fun testSimpleContainer() = noLegacyJs {
+ assertJsonFormAndRestored(
+ SimpleContainerForUInt.serializer(),
+ SimpleContainerForUInt(precedent),
+ """{"i":2147483657}""",
+ )
+ }
+
+ @Test
+ fun testSimpleContainerForMyTypeWithCustomSerializer() = assertJsonFormAndRestored(
+ SimpleContainerForMyType.serializer(),
+ SimpleContainerForMyType(MyUInt(precedent.toInt())),
+ """{"i":2147483657}""",
+ )
+
+ @Test
+ fun testSimpleContainerForList() = noLegacyJs {
+ assertJsonFormAndRestored(
+ ContainerForList.serializer(UInt.serializer()),
+ ContainerForList(MyList(listOf(precedent))),
+ """{"i":[2147483657]}""",
+ )
+ }
+
+ @Test
+ fun testInlineClassesWithStrings() = noLegacyJs {
+ assertJsonFormAndRestored(
+ ResourceIdentifier.serializer(),
+ ResourceIdentifier(ResourceId("resId"), ResourceType("resType"), ValueWrapper(ResourceType("wrappedType"))),
+ """{"id":"resId","type":"resType","type2":"wrappedType"}"""
+ )
+ }
+
+ @Test
+ fun testUnsignedInBoxedPosition() = assertJsonFormAndRestored(
+ UnsignedInBoxedPosition.serializer(),
+ UnsignedInBoxedPosition(listOf(precedent)),
+ """{"i":[2147483657]}""",
+ )
+
+ @Test
+ fun testMixedPositions() {
+ val o = MixedPositions(
+ int = precedent.toInt(),
+ intNullable = precedent.toInt(),
+ uint = precedent,
+ uintNullable = precedent,
+ boxedInt = listOf(precedent.toInt()),
+ boxedUInt = listOf(precedent),
+ boxedNullableInt = listOf(null, precedent.toInt(), null),
+ boxedNullableUInt = listOf(null, precedent, null)
+ )
+ assertJsonFormAndRestored(
+ MixedPositions.serializer(),
+ o,
+ """{"int":-2147483639,"intNullable":-2147483639,"uint":2147483657,"uintNullable":2147483657,"boxedInt":[-2147483639],"boxedUInt":[2147483657],"boxedNullableInt":[null,-2147483639,null],"boxedNullableUInt":[null,2147483657,null]}""",
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/inline/UnsignedIntegersTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/inline/UnsignedIntegersTest.kt
new file mode 100644
index 00000000..26d67288
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/inline/UnsignedIntegersTest.kt
@@ -0,0 +1,59 @@
+@file:Suppress("INLINE_CLASSES_NOT_SUPPORTED", "SERIALIZER_NOT_FOUND")
+@file:OptIn(ExperimentalUnsignedTypes::class)
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features.inline
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+class UnsignedIntegersTest : JsonTestBase() {
+ @Serializable
+ data class AllUnsigned(
+ val uInt: UInt,
+ val uLong: ULong,
+ val uByte: UByte,
+ val uShort: UShort,
+ val signedInt: Int,
+ val signedLong: Long,
+ val double: Double
+ )
+
+ @Serializable
+ data class UnsignedWithoutLong(val uInt: UInt, val uByte: UByte, val uShort: UShort)
+
+ @Test
+ fun testUnsignedIntegersJson() {
+ val data = AllUnsigned(
+ Int.MAX_VALUE.toUInt() + 10.toUInt(),
+ Long.MAX_VALUE.toULong() + 10.toULong(),
+ 239.toUByte(),
+ 65000.toUShort(),
+ -42,
+ Long.MIN_VALUE,
+ 1.1
+ )
+ assertJsonFormAndRestored(
+ AllUnsigned.serializer(),
+ data,
+ """{"uInt":2147483657,"uLong":9223372036854775817,"uByte":239,"uShort":65000,"signedInt":-42,"signedLong":-9223372036854775808,"double":1.1}""",
+ )
+ }
+
+ @Test
+ fun testUnsignedIntegersWithoutLongJson() {
+ val data = UnsignedWithoutLong(
+ Int.MAX_VALUE.toUInt() + 10.toUInt(),
+ 239.toUByte(),
+ 65000.toUShort(),
+ )
+ assertJsonFormAndRestored(
+ UnsignedWithoutLong.serializer(),
+ data,
+ """{"uInt":2147483657,"uByte":239,"uShort":65000}""",
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/sealed/SealedChild.kt b/formats/json/commonTest/src/kotlinx/serialization/features/sealed/SealedChild.kt
new file mode 100644
index 00000000..1bb2b02b
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/sealed/SealedChild.kt
@@ -0,0 +1,8 @@
+package kotlinx.serialization.features.sealed
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+@SerialName("first child")
+data class SealedChild(val j: Int) : SealedParent(1)
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/sealed/SealedParent.kt b/formats/json/commonTest/src/kotlinx/serialization/features/sealed/SealedParent.kt
new file mode 100644
index 00000000..b096c120
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/sealed/SealedParent.kt
@@ -0,0 +1,6 @@
+package kotlinx.serialization.features.sealed
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+sealed class SealedParent(val i: Int)
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/AbstractJsonImplicitNullsTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/AbstractJsonImplicitNullsTest.kt
new file mode 100644
index 00000000..a2f4a9df
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/AbstractJsonImplicitNullsTest.kt
@@ -0,0 +1,151 @@
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+/*
+ * Actual testing should be performed in subclasses.
+ * Subclasses implement serialization and deserialization for different types: serial Kotlin classes, JsonElement, dynamic etc
+ */
+@Ignore
+abstract class AbstractJsonImplicitNullsTest {
+ @Serializable
+ data class Nullable(
+ val f0: Int?,
+ val f1: Int?,
+ val f2: Int?,
+ val f3: Int?,
+ )
+
+ @Serializable
+ data class WithNotNull(
+ val f0: Int?,
+ val f1: Int?,
+ val f2: Int,
+ )
+
+ @Serializable
+ data class WithOptional(
+ val f0: Int?,
+ val f1: Int? = 1,
+ val f2: Int = 2,
+ )
+
+ @Serializable
+ data class Outer(val i: Inner)
+
+ @Serializable
+ data class Inner(val s1: String?, val s2: String?)
+
+ @Serializable
+ data class ListWithNullable(val l: List<Int?>)
+
+ @Serializable
+ data class MapWithNullable(val m: Map<Int?, Int?>)
+
+ @Serializable
+ data class NullableList(val l: List<Int>?)
+
+ @Serializable
+ data class NullableMap(val m: Map<Int, Int>?)
+
+
+ private val format = Json { explicitNulls = false }
+
+ protected abstract fun <T> Json.encode(value: T, serializer: KSerializer<T>): String
+
+ protected abstract fun <T> Json.decode(json: String, serializer: KSerializer<T>): T
+
+ @Test
+ fun testExplicit() {
+ val plain = Nullable(null, 10, null, null)
+ val json = """{"f0":null,"f1":10,"f2":null,"f3":null}"""
+
+ assertEquals(json, Json.encode(plain, Nullable.serializer()))
+ assertEquals(plain, Json.decode(json, Nullable.serializer()))
+ }
+
+ @Test
+ fun testNullable() {
+ val plain = Nullable(null, 10, null, null)
+ val json = """{"f1":10}"""
+
+ assertEquals(json, format.encode(plain, Nullable.serializer()))
+ assertEquals(plain, format.decode(json, Nullable.serializer()))
+ }
+
+ @Test
+ fun testMissingNotNull() {
+ val json = """{"f1":10}"""
+
+ assertFailsWith(SerializationException::class) {
+ format.decode(json, WithNotNull.serializer())
+ }
+ }
+
+ @Test
+ fun testDecodeOptional() {
+ val json = """{}"""
+
+ val decoded = format.decode(json, WithOptional.serializer())
+ assertEquals(WithOptional(null), decoded)
+ }
+
+
+ @Test
+ fun testNestedJsonObject() {
+ val json = """{"i": {}}"""
+
+ val decoded = format.decode(json, Outer.serializer())
+ assertEquals(Outer(Inner(null, null)), decoded)
+ }
+
+ @Test
+ fun testListWithNullable() {
+ val jsonWithNull = """{"l":[null]}"""
+ val jsonWithEmptyList = """{"l":[]}"""
+
+ val encoded = format.encode(ListWithNullable(listOf(null)), ListWithNullable.serializer())
+ assertEquals(jsonWithNull, encoded)
+
+ val decoded = format.decode(jsonWithEmptyList, ListWithNullable.serializer())
+ assertEquals(ListWithNullable(emptyList()), decoded)
+ }
+
+ @Test
+ fun testMapWithNullable() {
+ val jsonWithNull = """{"m":{null:null}}"""
+ val jsonWithQuotedNull = """{"m":{"null":null}}"""
+ val jsonWithEmptyList = """{"m":{}}"""
+
+ val encoded = format.encode(MapWithNullable(mapOf(null to null)), MapWithNullable.serializer())
+ //Json encode map null key as `null:` but other external utilities may encode it as a String `"null":`
+ assertTrue { listOf(jsonWithNull, jsonWithQuotedNull).contains(encoded) }
+
+ val decoded = format.decode(jsonWithEmptyList, MapWithNullable.serializer())
+ assertEquals(MapWithNullable(emptyMap()), decoded)
+ }
+
+ @Test
+ fun testNullableList() {
+ val json = """{}"""
+
+ val encoded = format.encode(NullableList(null), NullableList.serializer())
+ assertEquals(json, encoded)
+
+ val decoded = format.decode(json, NullableList.serializer())
+ assertEquals(NullableList(null), decoded)
+ }
+
+ @Test
+ fun testNullableMap() {
+ val json = """{}"""
+
+ val encoded = format.encode(NullableMap(null), NullableMap.serializer())
+ assertEquals(json, encoded)
+
+ val decoded = format.decode(json, NullableMap.serializer())
+ assertEquals(NullableMap(null), decoded)
+ }
+
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/BasicTypesSerializationTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/BasicTypesSerializationTest.kt
new file mode 100644
index 00000000..dee87fd6
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/BasicTypesSerializationTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class BasicTypesSerializationTest : JsonTestBase() {
+
+ val goldenValue = """
+ {"unit":{},"boolean":true,"byte":10,"short":20,"int":30,"long":40,"float":50.1,"double":60.1,"char":"A","string":"Str0","enum":"POSITIVE","intData":{"intV":70},"unitN":null,"booleanN":null,"byteN":11,"shortN":21,"intN":31,"longN":41,"floatN":51.1,"doubleN":61.1,"charN":"B","stringN":"Str1","enumN":"NEUTRAL","intDataN":null,"listInt":[1,2,3],"listIntN":[4,5,null],"listNInt":[6,7,8],"listNIntN":[null,9,10],"listListEnumN":[["NEGATIVE",null]],"listIntData":[{"intV":1},{"intV":2},{"intV":3}],"listIntDataN":[{"intV":1},null,{"intV":3}],"tree":{"name":"root","left":{"name":"left","left":null,"right":null},"right":{"name":"right","left":{"name":"right.left","left":null,"right":null},"right":{"name":"right.right","left":null,"right":null}}},"mapStringInt":{"one":1,"two":2,"three":3},"mapIntStringN":{"0":null,"1":"first","2":"second"},"arrays":{"arrByte":[1,2,3],"arrInt":[100,200,300],"arrIntN":[null,-1,-2],"arrIntData":[{"intV":1},{"intV":2}]}}
+ """.trimIndent()
+
+ @Test
+ fun testSerialization() = parametrizedTest { jsonTestingMode ->
+ val json = default.encodeToString(TypesUmbrella.serializer(), umbrellaInstance)
+ assertEquals(goldenValue, json)
+ val instance = default.decodeFromString(TypesUmbrella.serializer(), json, jsonTestingMode)
+ assertEquals(umbrellaInstance, instance)
+ assertNotSame(umbrellaInstance, instance)
+ }
+
+ @Test
+ fun testTopLevelPrimitive() = parametrizedTest { jsonTestingMode ->
+ testPrimitive(Unit, "{}", jsonTestingMode)
+ testPrimitive(false, "false", jsonTestingMode)
+ testPrimitive(1.toByte(), "1", jsonTestingMode)
+ testPrimitive(2.toShort(), "2", jsonTestingMode)
+ testPrimitive(3, "3", jsonTestingMode)
+ testPrimitive(4L, "4", jsonTestingMode)
+ testPrimitive(5.1f, "5.1", jsonTestingMode)
+ testPrimitive(6.1, "6.1", jsonTestingMode)
+ testPrimitive('c', "\"c\"", jsonTestingMode)
+ testPrimitive("string", "\"string\"", jsonTestingMode)
+ }
+
+ private inline fun <reified T : Any> testPrimitive(primitive: T, expectedJson: String, jsonTestingMode: JsonTestingMode) {
+ val json = default.encodeToString(primitive, jsonTestingMode)
+ assertEquals(expectedJson, json)
+ val instance = default.decodeFromString<T>(json, jsonTestingMode)
+ assertEquals(primitive, instance)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/DecodeFromJsonElementTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/DecodeFromJsonElementTest.kt
new file mode 100644
index 00000000..5db3d773
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/DecodeFromJsonElementTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class DecodeFromJsonElementTest {
+ @Serializable
+ data class A(val a: Int)
+
+ @Serializable
+ data class B(val a: A?)
+
+ @Test
+ fun testDecodeTopLevelNullable() {
+ val a = A(42)
+ val jsonElement = Json.encodeToJsonElement(a)
+ assertEquals(a, Json.decodeFromJsonElement<A?>(jsonElement))
+ }
+
+ @Test
+ fun topLevelNull() {
+ assertNull(Json.decodeFromJsonElement<A?>(JsonNull))
+ }
+
+ @Test
+ fun testInnerNullable() {
+ val b = B(A(42))
+ val json = Json.encodeToJsonElement(b)
+ assertEquals(b, Json.decodeFromJsonElement(json))
+ }
+
+ @Test
+ fun testInnerNullableNull() {
+ val b = B(null)
+ val json = Json.encodeToJsonElement(b)
+ assertEquals(b, Json.decodeFromJsonElement(json))
+ }
+
+ @Test
+ fun testPrimitive() {
+ assertEquals(42, Json.decodeFromJsonElement(JsonPrimitive(42)))
+ assertEquals(42, Json.decodeFromJsonElement<Int?>(JsonPrimitive(42)))
+ assertEquals(null, Json.decodeFromJsonElement<Int?>(JsonNull))
+ }
+
+ @Test
+ fun testNullableList() {
+ assertEquals(listOf(42), Json.decodeFromJsonElement<List<Int>?>(JsonArray(listOf(JsonPrimitive(42)))))
+ assertEquals(listOf(42), Json.decodeFromJsonElement<List<Int?>?>(JsonArray(listOf(JsonPrimitive(42)))))
+ assertEquals(listOf(42), Json.decodeFromJsonElement<List<Int?>>(JsonArray(listOf(JsonPrimitive(42)))))
+ // Nulls
+ assertEquals(null, Json.decodeFromJsonElement<List<Int>?>(JsonNull))
+ assertEquals(null, Json.decodeFromJsonElement<List<Int?>?>(JsonNull))
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonBuildersTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonBuildersTest.kt
new file mode 100644
index 00000000..26248210
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonBuildersTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlin.test.*
+
+class JsonBuildersTest {
+
+ @Test
+ fun testBuildJson() {
+ val json = buildJsonObject {
+ putJsonObject("object") {
+ put("k", JsonPrimitive("v"))
+ }
+
+ putJsonArray("array") {
+ addJsonObject { put("nestedLiteral", true) }
+ }
+
+ val number: Number? = null
+ put("null", number)
+ put("primitive", JsonPrimitive(42))
+ put("boolean", true)
+ put("literal", "foo")
+ }
+ assertEquals("""{"object":{"k":"v"},"array":[{"nestedLiteral":true}],"null":null,"primitive":42,"boolean":true,"literal":"foo"}""", json.toString())
+ }
+
+ @Test
+ fun testBuildJsonArray() {
+ val json = buildJsonArray {
+ add(true)
+ addJsonArray {
+ for (i in 1..10) add(i)
+ }
+ addJsonObject {
+ put("stringKey", "stringValue")
+ }
+ }
+ assertEquals("""[true,[1,2,3,4,5,6,7,8,9,10],{"stringKey":"stringValue"}]""", json.toString())
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonCoerceInputValuesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonCoerceInputValuesTest.kt
new file mode 100644
index 00000000..1e6b20de
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonCoerceInputValuesTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.internal.*
+import kotlin.test.*
+
+class JsonCoerceInputValuesTest : JsonTestBase() {
+ @Serializable
+ data class WithBoolean(val b: Boolean = false)
+
+ @Serializable
+ data class WithEnum(val e: SampleEnum = SampleEnum.OptionC)
+
+ @Serializable
+ data class MultipleValues(
+ val data: StringData,
+ val data2: IntData = IntData(0),
+ val i: Int = 42,
+ val e: SampleEnum = SampleEnum.OptionA,
+ val foo: String
+ )
+
+ val json = Json {
+ coerceInputValues = true
+ isLenient = true
+ }
+
+ private fun <T> doTest(inputs: List<String>, expected: T, serializer: KSerializer<T>) {
+ for (input in inputs) {
+ parametrizedTest(json) {
+ assertEquals(expected, decodeFromString(serializer, input), "Failed on input: $input")
+ }
+ }
+ }
+
+ @Test
+ fun testUseDefaultOnNonNullableBoolean() = doTest(
+ listOf(
+ """{"b":false}""",
+ """{"b":null}""",
+ """{}""",
+ ),
+ WithBoolean(),
+ WithBoolean.serializer()
+ )
+
+ @Test
+ fun testUseDefaultOnUnknownEnum() {
+ doTest(
+ listOf(
+ """{"e":unknown_value}""",
+ """{"e":"unknown_value"}""",
+ """{"e":null}""",
+ """{}""",
+ ),
+ WithEnum(),
+ WithEnum.serializer()
+ )
+ assertFailsWith<JsonDecodingException> {
+ json.decodeFromString(WithEnum.serializer(), """{"e":{"x":"definitely not a valid enum value"}}""")
+ }
+ assertFailsWith<JsonDecodingException> { // test user still sees exception on missing quotes
+ Json(json) { isLenient = false }.decodeFromString(WithEnum.serializer(), """{"e":unknown_value}""")
+ }
+ }
+
+ @Test
+ fun testUseDefaultInMultipleCases() {
+ val testData = mapOf(
+ """{"data":{"data":"foo"},"data2":null,"i":null,"e":null,"foo":"bar"}""" to MultipleValues(
+ StringData("foo"),
+ foo = "bar"
+ ),
+ """{"data":{"data":"foo"},"data2":{"intV":42},"i":null,"e":null,"foo":"bar"}""" to MultipleValues(
+ StringData(
+ "foo"
+ ), IntData(42), foo = "bar"
+ ),
+ """{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"NoOption","foo":"bar"}""" to MultipleValues(
+ StringData("foo"),
+ IntData(42),
+ i = 0,
+ foo = "bar"
+ ),
+ """{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"OptionC","foo":"bar"}""" to MultipleValues(
+ StringData("foo"),
+ IntData(42),
+ i = 0,
+ e = SampleEnum.OptionC,
+ foo = "bar"
+ ),
+ )
+ for ((input, expected) in testData) {
+ assertEquals(expected, json.decodeFromString(MultipleValues.serializer(), input), "Failed on input: $input")
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonConfigurationTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonConfigurationTest.kt
new file mode 100644
index 00000000..2a4dc2c1
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonConfigurationTest.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlin.test.*
+
+class JsonConfigurationTest {
+
+ @Test
+ fun testPrettyPrint() {
+ json(true, "")
+ json(true, "\n")
+ json(true, "\r")
+ json(true, "\t")
+ json(true, " ")
+ json(true, " ")
+ json(true, " \t\r\n\t ")
+ assertFailsWith<IllegalArgumentException> { json(false, " ") }
+ assertFailsWith<IllegalArgumentException> { json(false, " ") }
+ assertFailsWith<IllegalArgumentException> { json(true, "f") }
+ assertFailsWith<IllegalArgumentException> { json(true, "\tf\n") }
+ }
+
+ private fun json(prettyPrint: Boolean, prettyPrintIndent: String) = Json {
+ this.prettyPrint = prettyPrint
+ this.prettyPrintIndent = prettyPrintIndent
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonCustomSerializersTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonCustomSerializersTest.kt
new file mode 100644
index 00000000..91b65d9b
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonCustomSerializersTest.kt
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:UseContextualSerialization(JsonCustomSerializersTest.B::class)
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class JsonCustomSerializersTest : JsonTestBase() {
+
+ @Serializable
+ data class A(@Id(1) val b: B)
+
+ data class B(@Id(1) val value: Int)
+
+ object BSerializer : KSerializer<B> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("B", PrimitiveKind.INT)
+ override fun serialize(encoder: Encoder, value: B) {
+ encoder.encodeInt(value.value)
+ }
+
+ override fun deserialize(decoder: Decoder): B {
+ return B(decoder.decodeInt())
+ }
+ }
+
+ @Serializable
+ data class BList(@Id(1) val bs: List<B>)
+
+ @Serializable
+ data class C(@Id(1) val a: Int = 31, @Id(2) val b: Int = 42) {
+ @Serializer(forClass = C::class)
+ companion object : KSerializer<C> {
+ override fun serialize(encoder: Encoder, value: C) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ elemOutput.encodeIntElement(descriptor, 1, value.b)
+ if (value.a != 31) elemOutput.encodeIntElement(descriptor, 0, value.a)
+ elemOutput.endStructure(descriptor)
+ }
+ }
+ }
+
+ @Serializable
+ data class CList1(@Id(1) val c: List<C>)
+
+ @Serializable
+ data class CList2(@Id(1) val d: Int = 5, @Id(2) val c: List<C>) {
+ @Serializer(forClass = CList2::class)
+ companion object : KSerializer<CList2> {
+ override fun serialize(encoder: Encoder, value: CList2) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ elemOutput.encodeSerializableElement(descriptor, 1, ListSerializer(C), value.c)
+ if (value.d != 5) elemOutput.encodeIntElement(descriptor, 0, value.d)
+ elemOutput.endStructure(descriptor)
+ }
+ }
+ }
+
+ @Serializable
+ data class CList3(@Id(1) val e: List<C> = emptyList(), @Id(2) val f: Int) {
+ @Serializer(forClass = CList3::class)
+ companion object : KSerializer<CList3> {
+ override fun serialize(encoder: Encoder, value: CList3) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ if (value.e.isNotEmpty()) elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(C), value.e)
+ elemOutput.encodeIntElement(descriptor, 1, value.f)
+ elemOutput.endStructure(descriptor)
+ }
+ }
+ }
+
+ @Serializable
+ data class CList4(@Id(1) val g: List<C> = emptyList(), @Id(2) val h: Int) {
+ @Serializer(forClass = CList4::class)
+ companion object : KSerializer<CList4> {
+ override fun serialize(encoder: Encoder, value: CList4) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ elemOutput.encodeIntElement(descriptor, 1, value.h)
+ if (value.g.isNotEmpty()) elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(C), value.g)
+ elemOutput.endStructure(descriptor)
+ }
+ }
+ }
+
+ @Serializable
+ data class CList5(@Id(1) val g: List<Int> = emptyList(), @Id(2) val h: Int) {
+ @Serializer(forClass = CList5::class)
+ companion object : KSerializer<CList5> {
+ override fun serialize(encoder: Encoder, value: CList5) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ elemOutput.encodeIntElement(descriptor, 1, value.h)
+ if (value.g.isNotEmpty()) elemOutput.encodeSerializableElement(
+ descriptor, 0, ListSerializer(Int.serializer()),
+ value.g
+ )
+ elemOutput.endStructure(descriptor)
+ }
+ }
+ }
+
+ private val moduleWithB = serializersModuleOf(B::class, BSerializer)
+
+ private fun createJsonWithB() = Json { isLenient = true; serializersModule = moduleWithB; useAlternativeNames = false }
+ // useAlternativeNames uses SerialDescriptor.hashCode,
+ // which is unavailable for partially-customized serializers such as in this file
+ private val jsonNoAltNames = Json { useAlternativeNames = false }
+
+ @Test
+ fun testWriteCustom() = parametrizedTest { jsonTestingMode ->
+ val a = A(B(2))
+ val j = createJsonWithB()
+ val s = j.encodeToString(a, jsonTestingMode)
+ assertEquals("""{"b":2}""", s)
+ }
+
+ @Test
+ fun testReadCustom() = parametrizedTest { jsonTestingMode ->
+ val a = A(B(2))
+ val j = createJsonWithB()
+ val s = j.decodeFromString<A>("{b:2}", jsonTestingMode)
+ assertEquals(a, s)
+ }
+
+ @Test
+ fun testWriteCustomList() = parametrizedTest { jsonTestingMode ->
+ val obj = BList(listOf(B(1), B(2), B(3)))
+ val j = createJsonWithB()
+ val s = j.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"bs":[1,2,3]}""", s)
+ }
+
+ @Test
+ fun testReadCustomList() = parametrizedTest { jsonTestingMode ->
+ val obj = BList(listOf(B(1), B(2), B(3)))
+ val j = createJsonWithB()
+ val bs = j.decodeFromString<BList>("{bs:[1,2,3]}", jsonTestingMode)
+ assertEquals(obj, bs)
+ }
+
+ @Test
+ fun testWriteCustomListRootLevel() = parametrizedTest { jsonTestingMode ->
+ val obj = listOf(B(1), B(2), B(3))
+ val j = createJsonWithB()
+ val s = j.encodeToString(ListSerializer(BSerializer), obj, jsonTestingMode)
+ assertEquals("[1,2,3]", s)
+ }
+
+ @Test
+ fun testReadCustomListRootLevel() = parametrizedTest { jsonTestingMode ->
+ val obj = listOf(B(1), B(2), B(3))
+ val j = createJsonWithB()
+ val bs = j.decodeFromString(ListSerializer(BSerializer), "[1,2,3]", jsonTestingMode)
+ assertEquals(obj, bs)
+ }
+
+ @Test
+ fun testWriteCustomInvertedOrder() = parametrizedTest { jsonTestingMode ->
+ val obj = C(1, 2)
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"b":2,"a":1}""", s)
+ }
+
+ @Test
+ fun testWriteCustomOmitDefault() = parametrizedTest { jsonTestingMode ->
+ val obj = C(b = 2)
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"b":2}""", s)
+ }
+
+ @Test
+ fun testReadCustomInvertedOrder() = parametrizedTest { jsonTestingMode ->
+ val obj = C(1, 2)
+ val s = jsonNoAltNames.decodeFromString<C>("""{"b":2,"a":1}""", jsonTestingMode)
+ assertEquals(obj, s)
+ }
+
+ @Test
+ fun testReadCustomOmitDefault() = parametrizedTest { jsonTestingMode ->
+ val obj = C(b = 2)
+ val s = jsonNoAltNames.decodeFromString<C>("""{"b":2}""", jsonTestingMode)
+ assertEquals(obj, s)
+ }
+
+ @Test
+ fun testWriteListOfOptional() = parametrizedTest { jsonTestingMode ->
+ val obj = listOf(C(a = 1), C(b = 2), C(3, 4))
+ val s = jsonNoAltNames.encodeToString(ListSerializer(C), obj, jsonTestingMode)
+ assertEquals("""[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]""", s)
+ }
+
+ @Test
+ fun testReadListOfOptional() = parametrizedTest { jsonTestingMode ->
+ val obj = listOf(C(a = 1), C(b = 2), C(3, 4))
+ val j = """[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]"""
+ val s = jsonNoAltNames.decodeFromString(ListSerializer<kotlinx.serialization.json.JsonCustomSerializersTest.C>(C), j, jsonTestingMode)
+ assertEquals(obj, s)
+ }
+
+ @Test
+ fun testWriteOptionalList1() = parametrizedTest { jsonTestingMode ->
+ val obj = CList1(listOf(C(a = 1), C(b = 2), C(3, 4)))
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"c":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]}""", s)
+ }
+
+ @Test
+ fun testWriteOptionalList1Quoted() = parametrizedTest { jsonTestingMode ->
+ val obj = CList1(listOf(C(a = 1), C(b = 2), C(3, 4)))
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"c":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]}""", s)
+ }
+
+ @Test
+ fun testReadOptionalList1() = parametrizedTest { jsonTestingMode ->
+ val obj = CList1(listOf(C(a = 1), C(b = 2), C(3, 4)))
+ val j = """{"c":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]}"""
+ assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode))
+ }
+
+ @Test
+ fun testWriteOptionalList2a() = parametrizedTest { jsonTestingMode ->
+ val obj = CList2(7, listOf(C(a = 5), C(b = 6), C(7, 8)))
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"c":[{"b":42,"a":5},{"b":6},{"b":8,"a":7}],"d":7}""", s)
+ }
+
+ @Test
+ fun testReadOptionalList2a() = parametrizedTest { jsonTestingMode ->
+ val obj = CList2(7, listOf(C(a = 5), C(b = 6), C(7, 8)))
+ val j = """{"c":[{"b":42,"a":5},{"b":6},{"b":8,"a":7}],"d":7}"""
+ assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode))
+ }
+
+ @Test
+ fun testWriteOptionalList2b() = parametrizedTest { jsonTestingMode ->
+ val obj = CList2(c = listOf(C(a = 5), C(b = 6), C(7, 8)))
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"c":[{"b":42,"a":5},{"b":6},{"b":8,"a":7}]}""", s)
+ }
+
+ @Test
+ fun testReadOptionalList2b() = parametrizedTest { jsonTestingMode ->
+ val obj = CList2(c = listOf(C(a = 5), C(b = 6), C(7, 8)))
+ val j = """{"c":[{"b":42,"a":5},{"b":6},{"b":8,"a":7}]}"""
+ assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode))
+ }
+
+ @Test
+ fun testWriteOptionalList3a() = parametrizedTest { jsonTestingMode ->
+ val obj = CList3(listOf(C(a = 1), C(b = 2), C(3, 4)), 99)
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"e":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}],"f":99}""", s)
+ }
+
+ @Test
+ fun testReadOptionalList3a() = parametrizedTest { jsonTestingMode ->
+ val obj = CList3(listOf(C(a = 1), C(b = 2), C(3, 4)), 99)
+ val j = """{"e":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}],"f":99}"""
+ assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode))
+ }
+
+ @Test
+ fun testWriteOptionalList3b() = parametrizedTest { jsonTestingMode ->
+ val obj = CList3(f = 99)
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"f":99}""", s)
+ }
+
+ @Test
+ fun testReadOptionalList3b() = parametrizedTest { jsonTestingMode ->
+ val obj = CList3(f = 99)
+ val j = """{"f":99}"""
+ assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode))
+ }
+
+ @Test
+ fun testWriteOptionalList4a() = parametrizedTest { jsonTestingMode ->
+ val obj = CList4(listOf(C(a = 1), C(b = 2), C(3, 4)), 54)
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"h":54,"g":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]}""", s)
+ }
+
+ @Test
+ fun testReadOptionalList4a() = parametrizedTest { jsonTestingMode ->
+ val obj = CList4(listOf(C(a = 1), C(b = 2), C(3, 4)), 54)
+ val j = """{"h":54,"g":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]}"""
+ assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode))
+ }
+
+ @Test
+ fun testWriteOptionalList4b() = parametrizedTest { jsonTestingMode ->
+ val obj = CList4(h = 97)
+ val j = """{"h":97}"""
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals(j, s)
+ }
+
+ @Test
+ fun testReadOptionalList4b() = parametrizedTest { jsonTestingMode ->
+ val obj = CList4(h = 97)
+ val j = """{"h":97}"""
+ assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode))
+ }
+
+ @Test
+ fun testWriteOptionalList5a() = parametrizedTest { jsonTestingMode ->
+ val obj = CList5(listOf(9, 8, 7, 6, 5), 5)
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"h":5,"g":[9,8,7,6,5]}""", s)
+ }
+
+ @Test
+ fun testReadOptionalList5a() = parametrizedTest { jsonTestingMode ->
+ val obj = CList5(listOf(9, 8, 7, 6, 5), 5)
+ val j = """{"h":5,"g":[9,8,7,6,5]}"""
+ assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode))
+ }
+
+ @Test
+ fun testWriteOptionalList5b() = parametrizedTest { jsonTestingMode ->
+ val obj = CList5(h = 999)
+ val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode)
+ assertEquals("""{"h":999}""", s)
+ }
+
+ @Test
+ fun testReadOptionalList5b() = parametrizedTest { jsonTestingMode ->
+ val obj = CList5(h = 999)
+ val j = """{"h":999}"""
+ assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode))
+ }
+
+ @Test
+ fun testMapBuiltinsTest() = parametrizedTest { jsonTestingMode ->
+ val map = mapOf(1 to "1", 2 to "2")
+ val serial = MapSerializer(Int.serializer(), String.serializer())
+ val s = jsonNoAltNames.encodeToString(serial, map, jsonTestingMode)
+ assertEquals("""{"1":"1","2":"2"}""", s)
+ }
+
+ @Test
+ fun testResolveAtRootLevel() = parametrizedTest { jsonTestingMode ->
+ val j = createJsonWithB()
+ val bs = j.decodeFromString<B>("1", jsonTestingMode)
+ assertEquals(B(1), bs)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonDefaultContextTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonDefaultContextTest.kt
new file mode 100644
index 00000000..af54c31b
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonDefaultContextTest.kt
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlin.test.*
+
+class JsonDefaultContextTest {
+
+ @Test
+ fun testRepeatedSerializer() {
+ // #616
+ val json = Json
+ Json { serializersModule = json.serializersModule }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonElementDecodingTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonElementDecodingTest.kt
new file mode 100644
index 00000000..d0b105a0
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonElementDecodingTest.kt
@@ -0,0 +1,54 @@
+package kotlinx.serialization.json
+
+import kotlinx.serialization.Serializable
+import kotlin.test.*
+
+class JsonElementDecodingTest : JsonTestBase() {
+
+ @Serializable
+ data class A(val a: Int = 42)
+
+ @Test
+ fun testTopLevelClass() = assertSerializedForm(A(), """{}""".trimMargin())
+
+ @Test
+ fun testTopLevelNullableClass() {
+ assertSerializedForm<A?>(A(), """{}""")
+ assertSerializedForm<A?>(null, "null")
+ }
+
+ @Test
+ fun testTopLevelPrimitive() = assertSerializedForm(42, """42""")
+
+ @Test
+ fun testTopLevelNullablePrimitive() {
+ assertSerializedForm<Int?>(42, """42""")
+ assertSerializedForm<Int?>(null, """null""")
+ }
+
+ @Test
+ fun testTopLevelList() = assertSerializedForm(listOf(42), """[42]""")
+
+ @Test
+ fun testTopLevelNullableList() {
+ assertSerializedForm<List<Int>?>(listOf(42), """[42]""")
+ assertSerializedForm<List<Int>?>(null, """null""")
+ }
+
+ private inline fun <reified T> assertSerializedForm(value: T, expectedString: String) {
+ val element = Json.encodeToJsonElement(value)
+ assertEquals(expectedString, element.toString())
+ assertEquals(value, Json.decodeFromJsonElement(element))
+ }
+
+ @Test
+ fun testDeepRecursion() {
+ // Reported as https://github.com/Kotlin/kotlinx.serialization/issues/1594
+ var json = """{ "a": %}"""
+ for (i in 0..12) {
+ json = json.replace("%", json)
+ }
+ json = json.replace("%", "0")
+ Json.parseToJsonElement(json)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonEncoderDecoderRecursiveTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonEncoderDecoderRecursiveTest.kt
new file mode 100644
index 00000000..b4f7c716
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonEncoderDecoderRecursiveTest.kt
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlin.test.*
+
+class JsonEncoderDecoderRecursiveTest : JsonTestBase() {
+ private val inputDataString = """{"id":0,"payload":{"from":42,"to":43,"msg":"Hello world"},"timestamp":1000}"""
+ private val inputErrorString = """{"id":1,"payload":{"error":"Connection timed out"},"timestamp":1001}"""
+ private val inputDataJson = default.parseToJsonElement(inputDataString)
+ private val inputErrorJson = default.parseToJsonElement(inputErrorString)
+ private val inputRecursive =
+ """{"type":"b","children":[{"type":"a","value":1},{"type":"a","value":2},{"type":"b","children":[]}]}"""
+ private val outputRecursive = SealedRecursive.B(
+ listOf(SealedRecursive.A(1), SealedRecursive.A(2), SealedRecursive.B(emptyList()))
+ )
+
+ @Test
+ fun testParseDataString() = parametrizedTest { streaming ->
+ val ev = default.decodeFromString(Event.serializer(), inputDataString, streaming)
+ with(ev) {
+ assertEquals(0, id)
+ assertEquals(Either.Right(Payload(42, 43, "Hello world")), payload)
+ assertEquals(1000, timestamp)
+ }
+ }
+
+ @Test
+ fun testParseErrorString() = parametrizedTest { jsonTestingMode ->
+ val ev = default.decodeFromString(Event.serializer(), inputErrorString, jsonTestingMode)
+ with(ev) {
+ assertEquals(1, id)
+ assertEquals(Either.Left("Connection timed out"), payload)
+ assertEquals(1001, timestamp)
+ }
+ }
+
+ @Test
+ fun testWriteDataString() = parametrizedTest { jsonTestingMode ->
+ val outputData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000)
+ val ev = default.encodeToString(Event.serializer(), outputData, jsonTestingMode)
+ assertEquals(inputDataString, ev)
+ }
+
+ @Test
+ fun testWriteDataStringIndented() = parametrizedTest { jsonTestingMode ->
+ val outputData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000)
+ val ev = Json { prettyPrint = true }.encodeToString(Event.serializer(), outputData, jsonTestingMode)
+ assertEquals("""{
+ | "id": 0,
+ | "payload": {
+ | "from": 42,
+ | "to": 43,
+ | "msg": "Hello world"
+ | },
+ | "timestamp": 1000
+ |}""".trimMargin(), ev)
+ }
+
+ @Test
+ fun testWriteErrorString() = parametrizedTest { jsonTestingMode ->
+ val outputError = Event(1, Either.Left("Connection timed out"), 1001)
+ val ev = default.encodeToString(Event.serializer(), outputError, jsonTestingMode)
+ assertEquals(inputErrorString, ev)
+ }
+
+ @Test
+ fun testParseDataJson() {
+ val ev = default.decodeFromJsonElement(Event.serializer(), inputDataJson)
+ with(ev) {
+ assertEquals(0, id)
+ assertEquals(Either.Right(Payload(42, 43, "Hello world")), payload)
+ assertEquals(1000, timestamp)
+ }
+ }
+
+ @Test
+ fun testParseErrorJson() {
+ val ev = default.decodeFromJsonElement(Event.serializer(), inputErrorJson)
+ with(ev) {
+ assertEquals(1, id)
+ assertEquals(Either.Left("Connection timed out"), payload)
+ assertEquals(1001, timestamp)
+ }
+ }
+
+ @Test
+ fun testWriteDataJson() {
+ val outputData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000)
+ val ev = default.encodeToJsonElement(Event.serializer(), outputData)
+ assertEquals(inputDataJson, ev)
+ }
+
+ @Test
+ fun testWriteErrorJson() {
+ val outputError = Event(1, Either.Left("Connection timed out"), 1001)
+ val ev = default.encodeToJsonElement(Event.serializer(), outputError)
+ assertEquals(inputErrorJson, ev)
+ }
+
+ @Test
+ fun testParseRecursive() = parametrizedTest { jsonTestingMode ->
+ val ev = default.decodeFromString(RecursiveSerializer, inputRecursive, jsonTestingMode)
+ assertEquals(outputRecursive, ev)
+ }
+
+ @Test
+ fun testWriteRecursive() = parametrizedTest { jsonTestingMode ->
+ val ev = default.encodeToString(RecursiveSerializer, outputRecursive, jsonTestingMode)
+ assertEquals(inputRecursive, ev)
+ }
+
+ @Serializable
+ private data class Payload(val from: Long, val to: Long, val msg: String)
+
+ private sealed class Either {
+ data class Left(val errorMsg: String): Either()
+ data class Right(val data: Payload): Either()
+ }
+
+ private object EitherSerializer: KSerializer<Either> {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("Either", PolymorphicKind.SEALED) {
+ val leftDescriptor = buildClassSerialDescriptor("Either.Left") {
+ element<String>("errorMsg")
+ }
+ val rightDescriptor = buildClassSerialDescriptor("Either.Right") {
+ element("data", Payload.serializer().descriptor)
+ }
+ element("left", leftDescriptor)
+ element("right", rightDescriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): Either {
+ val jsonReader = decoder as? JsonDecoder
+ ?: throw SerializationException("This class can be loaded only by JSON")
+ val tree = jsonReader.decodeJsonElement() as? JsonObject
+ ?: throw SerializationException("Expected JSON object")
+ if ("error" in tree) return Either.Left(tree.getValue("error").jsonPrimitive.content)
+ return Either.Right(decoder.json.decodeFromJsonElement(Payload.serializer(), tree))
+ }
+
+ override fun serialize(encoder: Encoder, value: Either) {
+ val jsonWriter = encoder as? JsonEncoder
+ ?: throw SerializationException("This class can be saved only by JSON")
+ val tree = when (value) {
+ is Either.Left -> JsonObject(mapOf("error" to JsonPrimitive(value.errorMsg)))
+ is Either.Right -> encoder.json.encodeToJsonElement(Payload.serializer(), value.data)
+ }
+ jsonWriter.encodeJsonElement(tree)
+ }
+ }
+
+ @Serializable
+ private data class Event(
+ val id: Int,
+ @Serializable(with = EitherSerializer::class) val payload: Either,
+ val timestamp: Long
+ )
+
+ @Serializable(with = RecursiveSerializer::class)
+ private sealed class SealedRecursive {
+ @Serializable
+ data class A(val value: Int) : SealedRecursive()
+
+ @Serializable
+ data class B(val children: List<SealedRecursive>) : SealedRecursive()
+ }
+
+ private object RecursiveSerializer : KSerializer<SealedRecursive> {
+ private const val typeAttribute = "type"
+ private const val typeNameA = "a"
+ private const val typeNameB = "b"
+
+ // TODO in builder is not suitable for recursive descriptors
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("SealedRecursive", PolymorphicKind.SEALED) {
+ element("a", SealedRecursive.A.serializer().descriptor)
+ element("b", SealedRecursive.B.serializer().descriptor)
+ }
+
+ override fun serialize(encoder: Encoder, value: SealedRecursive) {
+ if (encoder !is JsonEncoder) throw SerializationException("This class can be saved only by JSON")
+ val (tree, typeName) = when (value) {
+ is SealedRecursive.A -> encoder.json.encodeToJsonElement(SealedRecursive.A.serializer(), value) to typeNameA
+ is SealedRecursive.B -> encoder.json.encodeToJsonElement(SealedRecursive.B.serializer(), value) to typeNameB
+ }
+ val contents: MutableMap<String, JsonElement> = mutableMapOf(typeAttribute to JsonPrimitive(typeName))
+ contents.putAll(tree.jsonObject)
+ val element = JsonObject(contents)
+ encoder.encodeJsonElement(element)
+ }
+
+ override fun deserialize(decoder: Decoder): SealedRecursive {
+ val jsonReader = decoder as? JsonDecoder
+ ?: throw SerializationException("This class can be loaded only by JSON")
+ val tree = jsonReader.decodeJsonElement() as? JsonObject
+ ?: throw SerializationException("Expected JSON object")
+ val typeName = tree.getValue(typeAttribute).jsonPrimitive.content
+ val objTree = JsonObject(tree - typeAttribute)
+ return when (typeName) {
+ typeNameA -> decoder.json.decodeFromJsonElement(SealedRecursive.A.serializer(), objTree)
+ typeNameB -> decoder.json.decodeFromJsonElement(SealedRecursive.B.serializer(), objTree)
+ else -> throw SerializationException("Unknown type: $typeName")
+ }
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonGenericTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonGenericTest.kt
new file mode 100644
index 00000000..c4618086
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonGenericTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlin.test.*
+
+class JsonGenericTest : JsonTestBase() {
+
+ @Serializable
+ class Array2DBox(val arr: Array<Array<Double>>) {
+ override fun toString(): String {
+ return arr.contentDeepToString()
+ }
+ }
+
+ @Test
+ fun testWriteDefaultPair() = parametrizedTest { jsonTestingMode ->
+ val pair = 42 to "foo"
+ val serializer = PairSerializer(
+ Int.serializer(),
+ String.serializer()
+ )
+ val s = default.encodeToString(serializer, pair, jsonTestingMode)
+ assertEquals("""{"first":42,"second":"foo"}""", s)
+ val restored = default.decodeFromString(serializer, s, jsonTestingMode)
+ assertEquals(pair, restored)
+ }
+
+ @Test
+ fun testWritePlainTriple() = parametrizedTest { jsonTestingMode ->
+ val triple = Triple(42, "foo", false)
+ val serializer = TripleSerializer(
+ Int.serializer(),
+ String.serializer(),
+ Boolean.serializer()
+ )
+ val s = default.encodeToString(serializer, triple, jsonTestingMode)
+ assertEquals("""{"first":42,"second":"foo","third":false}""", s)
+ val restored = default.decodeFromString(serializer, s, jsonTestingMode)
+ assertEquals(triple, restored)
+ }
+
+ @Test
+ fun testRecursiveArrays() = parametrizedTest { jsonTestingMode ->
+ val arr = Array2DBox(arrayOf(arrayOf(2.1, 1.2), arrayOf(42.3, -3.4)))
+ val str = default.encodeToString(Array2DBox.serializer(), arr, jsonTestingMode)
+ assertEquals("""{"arr":[[2.1,1.2],[42.3,-3.4]]}""", str)
+ val restored = default.decodeFromString(Array2DBox.serializer(), str, jsonTestingMode)
+ assertTrue(arr.arr.contentDeepEquals(restored.arr))
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonImplicitNullsTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonImplicitNullsTest.kt
new file mode 100644
index 00000000..c06c058c
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonImplicitNullsTest.kt
@@ -0,0 +1,13 @@
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+
+class JsonImplicitNullsTest: AbstractJsonImplicitNullsTest() {
+ override fun <T> Json.encode(value: T, serializer: KSerializer<T>): String {
+ return encodeToString(serializer, value)
+ }
+
+ override fun <T> Json.decode(json: String, serializer: KSerializer<T>): T {
+ return decodeFromString(serializer, json)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonMapKeysTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonMapKeysTest.kt
new file mode 100644
index 00000000..e958cca7
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonMapKeysTest.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.PrimitiveKind
+import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.descriptors.buildSerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.modules.SerializersModule
+import kotlinx.serialization.test.*
+import kotlin.jvm.*
+import kotlin.test.*
+
+@JvmInline
+@Serializable
+value class ComplexCarrier(val c: IntData)
+
+@JvmInline
+@Serializable
+value class PrimitiveCarrier(val c: String)
+
+data class ContextualValue(val c: String) {
+ @Serializer(forClass = ContextualValue::class)
+ companion object: KSerializer<ContextualValue> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ContextualValue", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: ContextualValue) {
+ encoder.encodeString(value.c)
+ }
+
+ override fun deserialize(decoder: Decoder): ContextualValue {
+ return ContextualValue(decoder.decodeString())
+ }
+ }
+}
+
+class JsonMapKeysTest : JsonTestBase() {
+ @Serializable
+ private data class WithMap(val map: Map<Long, Long>)
+
+ @Serializable
+ private data class WithValueKeyMap(val map: Map<PrimitiveCarrier, Long>)
+
+ @Serializable
+ private data class WithEnum(val map: Map<SampleEnum, Long>)
+
+ @Serializable
+ private data class WithComplexKey(val map: Map<IntData, String>)
+
+ @Serializable
+ private data class WithComplexValueKey(val map: Map<ComplexCarrier, String>)
+
+ @Serializable
+ private data class WithContextualValueKey(val map: Map<@Contextual PrimitiveCarrier, Long>)
+
+ @Serializable
+ private data class WithContextualKey(val map: Map<@Contextual ContextualValue, Long>)
+
+ @Test
+ fun testMapKeysShouldBeStrings() = parametrizedTest(default) {
+ assertStringFormAndRestored(
+ """{"map":{"10":10,"20":20}}""",
+ WithMap(mapOf(10L to 10L, 20L to 20L)),
+ WithMap.serializer(),
+ this
+ )
+ }
+
+ @Test
+ fun testStructuredMapKeysShouldBeProhibitedByDefault() = parametrizedTest { streaming ->
+ noLegacyJs {
+ verifyProhibition(WithComplexKey(mapOf(IntData(42) to "42")), streaming)
+ verifyProhibition(WithComplexValueKey(mapOf(ComplexCarrier(IntData(42)) to "42")), streaming)
+ }
+ }
+
+ private inline fun <reified T: Any> verifyProhibition(value: T, streaming: JsonTestingMode) {
+ val e = assertFailsWith<JsonException> {
+ Json.encodeToString(value, streaming)
+ }
+ assertTrue(e.message?.contains("can't be used in JSON as a key in the map") == true)
+ }
+
+ @Test
+ fun testStructuredMapKeysAllowedWithFlag() = assertJsonFormAndRestored(
+ WithComplexKey.serializer(),
+ WithComplexKey(mapOf(IntData(42) to "42")),
+ """{"map":[{"intV":42},"42"]}""",
+ Json { allowStructuredMapKeys = true }
+ )
+
+ @Test
+ fun testStructuredValueMapKeysAllowedWithFlag() = noLegacyJs {
+ assertJsonFormAndRestored(
+ WithComplexValueKey.serializer(),
+ WithComplexValueKey(mapOf(ComplexCarrier(IntData(42)) to "42")),
+ """{"map":[{"intV":42},"42"]}""",
+ Json { allowStructuredMapKeys = true }
+ )
+ }
+
+ @Test
+ fun testEnumsAreAllowedAsMapKeys() = assertJsonFormAndRestored(
+ WithEnum.serializer(),
+ WithEnum(mapOf(SampleEnum.OptionA to 1L, SampleEnum.OptionC to 3L)),
+ """{"map":{"OptionA":1,"OptionC":3}}""",
+ Json
+ )
+
+ @Test
+ fun testPrimitivesAreAllowedAsValueMapKeys() = noLegacyJs {
+ assertJsonFormAndRestored(
+ WithValueKeyMap.serializer(),
+ WithValueKeyMap(mapOf(PrimitiveCarrier("fooKey") to 1)),
+ """{"map":{"fooKey":1}}""",
+ Json
+ )
+ }
+
+ @Test
+ fun testContextualValuePrimitivesAreAllowedAsValueMapKeys() = noLegacyJs {
+ assertJsonFormAndRestored(
+ WithContextualValueKey.serializer(),
+ WithContextualValueKey(mapOf(PrimitiveCarrier("fooKey") to 1)),
+ """{"map":{"fooKey":1}}""",
+ Json {
+ serializersModule =
+ SerializersModule { contextual(PrimitiveCarrier::class, PrimitiveCarrier.serializer()) }
+ }
+ )
+ }
+
+ @Test
+ fun testContextualPrimitivesAreAllowedAsValueMapKeys() {
+ assertJsonFormAndRestored(
+ WithContextualKey.serializer(),
+ WithContextualKey(mapOf(ContextualValue("fooKey") to 1)),
+ """{"map":{"fooKey":1}}""",
+ Json {
+ serializersModule = SerializersModule { contextual(ContextualValue::class, ContextualValue.Companion) }
+ }
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonModesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonModesTest.kt
new file mode 100644
index 00000000..97993802
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonModesTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class JsonModesTest : JsonTestBase() {
+
+ @Test
+ fun testNan() = parametrizedTest(lenient) {
+ assertStringFormAndRestored("{\"double\":NaN,\"float\":NaN}", Box(Double.NaN, Float.NaN), Box.serializer())
+ }
+
+ @Test
+ fun testInfinity() = parametrizedTest(lenient) {
+ assertStringFormAndRestored(
+ "{\"double\":Infinity,\"float\":-Infinity}",
+ Box(Double.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY),
+ Box.serializer()
+ )
+ }
+
+ @Test
+ fun nonStrictJsonCanSkipValues() = parametrizedTest { jsonTestingMode ->
+ val data = JsonOptionalTests.Data()
+ assertEquals(
+ lenient.decodeFromString(JsonOptionalTests.Data.serializer(), "{strangeField: 100500, a:0}", jsonTestingMode),
+ data
+ )
+ assertEquals(
+ lenient.decodeFromString(JsonOptionalTests.Data.serializer(), "{a:0, strangeField: 100500}", jsonTestingMode),
+ data
+ )
+ }
+
+ @Test
+ fun nonStrictJsonCanSkipComplexValues() = parametrizedTest { jsonTestingMode ->
+ val data = JsonOptionalTests.Data()
+
+ assertEquals(
+ lenient.decodeFromString(
+ JsonOptionalTests.Data.serializer(),
+ "{a: 0, strangeField: {a: b, c: {d: e}, f: [g,h,j] }}",
+ jsonTestingMode
+ ),
+ data
+ )
+ assertEquals(
+ lenient.decodeFromString(
+ JsonOptionalTests.Data.serializer(),
+ "{strangeField: {a: b, c: {d: e}, f: [g,h,j] }, a: 0}",
+ jsonTestingMode
+ ),
+ data
+ )
+ }
+
+ @Test
+ fun ignoreKeysCanIgnoreWeirdStringValues() {
+ val data = JsonOptionalTests.Data()
+ fun doTest(input: String) {
+ assertEquals(data, lenient.decodeFromString(input))
+ }
+ doTest("{a: 0, strangeField: [\"imma string with } bracket\", \"sss\"]}")
+ doTest("{a: 0, strangeField: [\"imma string with ] bracket\", \"sss\"]}")
+ doTest("{a: 0, strangeField: \"imma string with } bracket\"}")
+ doTest("{a: 0, strangeField: \"imma string with ] bracket\"}")
+ doTest("{a: 0, strangeField: {key: \"imma string with ] bracket\"}}")
+ doTest("{a: 0, strangeField: {key: \"imma string with } bracket\"}}")
+ doTest("""{"a": 0, "strangeField": {"key": "imma string with } bracket"}}""")
+ doTest("""{"a": 0, "strangeField": {"key": "imma string with ] bracket"}}""")
+ doTest("""{"a": 0, "strangeField": ["imma string with ] bracket"]}""")
+ doTest("""{"a": 0, "strangeField": ["imma string with } bracket"]}""")
+ }
+
+ @Serializable
+ class Empty
+
+ @Test
+ fun lenientThrowOnMalformedString() {
+ fun doTest(input: String) {
+ assertFailsWith<SerializationException> { lenient.decodeFromString(Empty.serializer(), input) }
+ }
+ doTest("""{"a":[{"b":[{"c":{}d",""e"":"}]}""")
+ doTest("""{"a":[}""")
+ doTest("""{"a":""")
+ lenient.decodeFromString(Empty.serializer(), """{"a":[]}""") // should not throw
+ }
+
+ @Test
+ fun testSerializeQuotedJson() = parametrizedTest { jsonTestingMode ->
+ assertEquals(
+ """{"a":10,"e":false,"c":"Hello"}""", default.encodeToString(
+ JsonTransientTest.Data.serializer(),
+ JsonTransientTest.Data(10, 100), jsonTestingMode
+ )
+ )
+ }
+
+ @Test
+ fun testStrictJsonCanNotSkipValues() = parametrizedTest { jsonTestingMode ->
+ assertFailsWith(SerializationException::class) {
+ default.decodeFromString(JsonOptionalTests.Data.serializer(), "{strangeField: 100500, a:0}", jsonTestingMode)
+ }
+ }
+
+ @Serializable
+ data class Box(val double: Double, val float: Float)
+
+
+ @Serializable
+ object Object
+
+ @Serializable
+ data class Holder(val o: Object)
+
+ @Test
+ fun testIgnoreUnknownKeysObject() = parametrizedTest { jsonTestingMode ->
+ noLegacyJs {
+ assertEquals(Holder(Object), lenient.decodeFromString("""{"o":{}}""", jsonTestingMode))
+ assertEquals(Holder(Object), lenient.decodeFromString("""{"o":{"unknown":{"b":"c"}}}""", jsonTestingMode))
+ assertEquals(Object, lenient.decodeFromString("""{}""", jsonTestingMode))
+ assertEquals(Object, lenient.decodeFromString("""{"o":{"unknown":{"b":"c"}}}""", jsonTestingMode))
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonNumericKeysTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonNumericKeysTest.kt
new file mode 100644
index 00000000..ee3e8f15
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonNumericKeysTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.serializer
+import kotlin.test.*
+
+class JsonNumericKeysTest : JsonTestBase() {
+ @Serializable
+ data class EntryWrapper(val e: Map.Entry<Int, Int>)
+
+ @Serializable
+ data class MapWrapper(val m: Map<Int, Int>)
+
+ @Test
+ fun testIntegerKeyInTopLevelEntry() {
+ assertJsonFormAndRestored(MapEntrySerializer(Int.serializer(), Int.serializer()), getEntry(), """{"1":2}""")
+ }
+
+ @Test
+ fun testIntegerKeyInEntry() {
+ assertJsonFormAndRestored(EntryWrapper.serializer(), EntryWrapper(getEntry()), """{"e":{"1":2}}""")
+ }
+
+ @Test
+ fun testIntegerKeyInTopLevelMap() = parametrizedTest {
+ assertJsonFormAndRestored(serializer(), mapOf(1 to 2), """{"1":2}""")
+
+ }
+
+ @Test
+ fun testIntegerKeyInMap() = parametrizedTest {
+ assertJsonFormAndRestored(MapWrapper.serializer(), MapWrapper(mapOf(1 to 2)), """{"m":{"1":2}}""")
+ }
+
+ // Workaround equals on JS and Native
+ fun getEntry(): Map.Entry<Int, Int> {
+ val e = default.decodeFromString(MapEntrySerializer(Int.serializer(), Int.serializer()), """{"1":2}""")
+ assertEquals(1, e.key)
+ assertEquals(2, e.value)
+ return e
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonOptionalTests.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonOptionalTests.kt
new file mode 100644
index 00000000..679a972b
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonOptionalTests.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class JsonOptionalTests : JsonTestBase() {
+
+ @Suppress("EqualsOrHashCode")
+ @Serializable
+ internal class Data(@Required val a: Int = 0, val b: Int = 42) {
+
+ var c = "Hello"
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as Data
+
+ if (a != other.a) return false
+ if (b != other.b) return false
+ if (c != other.c) return false
+
+ return true
+ }
+ }
+
+ @Test
+ fun testAll() = parametrizedTest { jsonTestingMode ->
+ assertEquals("""{"a":0,"b":42,"c":"Hello"}""",
+ default.encodeToString(Data.serializer(), Data(), jsonTestingMode))
+ assertEquals(lenient.decodeFromString(Data.serializer(), "{a:0,b:43,c:Hello}", jsonTestingMode), Data(b = 43))
+ assertEquals(lenient.decodeFromString(Data.serializer(), "{a:0,b:42,c:Hello}", jsonTestingMode), Data())
+ }
+
+ @Test
+ fun testMissingOptionals() = parametrizedTest { jsonTestingMode ->
+ assertEquals(default.decodeFromString(Data.serializer(), """{"a":0,"c":"Hello"}""", jsonTestingMode), Data())
+ assertEquals(default.decodeFromString(Data.serializer(), """{"a":0}""", jsonTestingMode), Data())
+ }
+
+ @Test
+ fun testThrowMissingField() = parametrizedTest { jsonTestingMode ->
+ assertFailsWithMissingField {
+ lenient.decodeFromString(Data.serializer(), "{b:0}", jsonTestingMode)
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt
new file mode 100644
index 00000000..87b0f358
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class JsonParserFailureModesTest : JsonTestBase() {
+
+ @Serializable
+ data class Holder(
+ val id: Long
+ )
+
+ @Test
+ fun testFailureModes() = parametrizedTest {
+ assertFailsWith<JsonDecodingException> {
+ default.decodeFromString(
+ Holder.serializer(),
+ """{"id": "}""",
+ it
+ )
+ }
+ assertFailsWith<JsonDecodingException> {
+ default.decodeFromString(
+ Holder.serializer(),
+ """{"id": ""}""",
+ it
+ )
+ }
+ assertFailsWith<JsonDecodingException> {
+ default.decodeFromString(
+ Holder.serializer(),
+ """{"id":a}""",
+ it
+ )
+ }
+ assertFailsWith<JsonDecodingException> {
+ default.decodeFromString(
+ Holder.serializer(),
+ """{"id":2.0}""",
+ it
+ )
+ }
+ assertFailsWith<JsonDecodingException> {
+ default.decodeFromString(
+ Holder.serializer(),
+ """{"id2":2}""",
+ it
+ )
+ }
+ // 9223372036854775807 is Long.MAX_VALUE
+ assertFailsWith<JsonDecodingException> {
+ default.decodeFromString(
+ Holder.serializer(),
+ """{"id":${Long.MAX_VALUE}""" + "00" + "}",
+ it
+ )
+ }
+ // -9223372036854775808 is Long.MIN_VALUE
+ assertFailsWith<JsonDecodingException> {
+ default.decodeFromString(
+ Holder.serializer(),
+ """{"id":9223372036854775808}""",
+ it
+ )
+ }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(Holder.serializer(), """{"id"}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(Holder.serializer(), """{"id}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(Holder.serializer(), """{"i}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(Holder.serializer(), """{"}""", it) }
+ assertFailsWithMissingField { default.decodeFromString(Holder.serializer(), """{}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(Holder.serializer(), """{""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(Holder.serializer(), """}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(Holder.serializer(), """{""", it) }
+ }
+
+ @Serializable
+ class BooleanHolder(val b: Boolean)
+
+ @Test
+ fun testBoolean() = parametrizedTest {
+ assertFailsWith<JsonDecodingException> {
+ default.decodeFromString(
+ BooleanHolder.serializer(),
+ """{"b": fals}""",
+ it
+ )
+ }
+ assertFailsWith<JsonDecodingException> {
+ default.decodeFromString(
+ BooleanHolder.serializer(),
+ """{"b": 123}""",
+ it
+ )
+ }
+ }
+
+ @Serializable
+ class PrimitiveHolder(
+ val b: Byte = 0, val s: Short = 0, val i: Int = 0
+ )
+
+ @Test
+ fun testOverflow() = parametrizedTest {
+ // Byte overflow
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<PrimitiveHolder>("""{"b": 128}""", it) }
+ // Short overflow
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<PrimitiveHolder>("""{"s": 32768}""", it) }
+ // Int overflow
+ assertFailsWith<JsonDecodingException> {
+ default.decodeFromString<PrimitiveHolder>(
+ """{"i": 2147483648}""",
+ it
+ )
+ }
+ }
+
+ @Test
+ fun testNoOverflow() = parametrizedTest {
+ default.decodeFromString<PrimitiveHolder>("""{"b": ${Byte.MAX_VALUE}}""", it)
+ default.decodeFromString<PrimitiveHolder>("""{"b": ${Byte.MIN_VALUE}}""", it)
+ default.decodeFromString<PrimitiveHolder>("""{"s": ${Short.MAX_VALUE}}""", it)
+ default.decodeFromString<PrimitiveHolder>("""{"s": ${Short.MIN_VALUE}}""", it)
+ default.decodeFromString<PrimitiveHolder>("""{"i": ${Int.MAX_VALUE}}""", it)
+ default.decodeFromString<PrimitiveHolder>("""{"i": ${Int.MIN_VALUE}}""", it)
+ default.decodeFromString<Holder>("""{"id": ${Long.MIN_VALUE.toString()}}""", it)
+ default.decodeFromString<Holder>("""{"id": ${Long.MAX_VALUE.toString()}}""", it)
+ }
+
+ @Test
+ fun testInvalidNumber() = parametrizedTest {
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<Holder>("""{"id":-}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<Holder>("""{"id":+}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<Holder>("""{"id":--}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<Holder>("""{"id":1-1}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<Holder>("""{"id":0-1}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<Holder>("""{"id":0-}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<Holder>("""{"id":a}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<Holder>("""{"id":-a}""", it) }
+ }
+
+
+ @Serializable
+ data class BooleanWrapper(val b: Boolean)
+
+ @Serializable
+ data class StringWrapper(val s: String)
+
+ @Test
+ fun testUnexpectedNull() = parametrizedTest {
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<BooleanWrapper>("""{"b":{"b":"b"}}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<BooleanWrapper>("""{"b":null}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<StringWrapper>("""{"s":{"s":"s"}}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<StringWrapper>("""{"s":null}""", it) }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonParserTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonParserTest.kt
new file mode 100644
index 00000000..123214e2
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonParserTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class JsonParserTest : JsonTestBase() {
+
+ @Test
+ fun testQuotedBrace() {
+ val tree = parse("""{"x": "{"}""")
+ assertTrue("x" in tree)
+ assertEquals("{", (tree.getValue("x") as JsonLiteral).content)
+ }
+
+ private fun parse(input: String) = default.parseToJsonElement(input).jsonObject
+
+ @Test
+ fun testEmptyKey() {
+ val tree = parse("""{"":"","":""}""")
+ assertTrue("" in tree)
+ assertEquals("", (tree.getValue("") as JsonLiteral).content)
+ }
+
+ @Test
+ fun testEmptyValue() {
+ assertFailsWith<JsonDecodingException> {
+ parse("""{"X": "foo", "Y"}""")
+ }
+ }
+
+ @Test
+ fun testIncorrectUnicodeEscape() {
+ assertFailsWith<JsonDecodingException> {
+ parse("""{"X": "\uDD1H"}""")
+ }
+ }
+
+ @Test
+ fun testParseEscapedSymbols() {
+ assertEquals(
+ StringData("https://t.co/M1uhwigsMT"),
+ default.decodeFromString(StringData.serializer(), """{"data":"https:\/\/t.co\/M1uhwigsMT"}""")
+ )
+ assertEquals(StringData("\"test\""), default.decodeFromString(StringData.serializer(), """{"data": "\"test\""}"""))
+ assertEquals(StringData("\u00c9"), default.decodeFromString(StringData.serializer(), """{"data": "\u00c9"}"""))
+ assertEquals(StringData("""\\"""), default.decodeFromString(StringData.serializer(), """{"data": "\\\\"}"""))
+ }
+
+ @Test
+ fun testWorkWithNonAsciiSymbols() {
+ assertStringFormAndRestored(
+ """{"data":"РуÑÑкие Буквы 🤔"}""",
+ StringData("РуÑÑкие Буквы \uD83E\uDD14"),
+ StringData.serializer()
+ )
+ }
+
+ @Test
+ fun testUnicodeEscapes() {
+ val data = buildString {
+ append(1.toChar())
+ append(".")
+ append(0x20.toChar())
+ append(".")
+ append("\n")
+ }
+
+ assertJsonFormAndRestored(String.serializer(), data, "\"\\u0001. .\\n\"")
+ }
+
+ @Test
+ fun testTrailingComma() {
+ testTrailingComma("{\"id\":0,}")
+ testTrailingComma("{\"id\":0 ,}")
+ testTrailingComma("{\"id\":0 , ,}")
+ }
+
+ private fun testTrailingComma(content: String) {
+ val e = assertFailsWith<JsonDecodingException> { Json.parseToJsonElement(content) }
+ val msg = e.message!!
+ assertTrue(msg.contains("Unexpected trailing"))
+ }
+
+ @Test
+ fun testUnclosedStringLiteral() {
+ assertFailsWith<JsonDecodingException> {
+ parse("\"")
+ }
+
+ assertFailsWith<JsonDecodingException> {
+ parse("""{"id":"""")
+ }
+ }
+
+ @Test
+ fun testNullValue() {
+ val obj = Json.parseToJsonElement("""{"k":null}""").jsonObject
+ val value = obj["k"]!!
+ assertTrue { value is JsonNull }
+ assertFalse { value.jsonPrimitive.isString }
+ }
+
+ @Test
+ fun testNullStringValue() {
+ val obj = Json.parseToJsonElement("""{"k":"null"}""").jsonObject
+ val value = obj["k"]!!
+ assertFalse { value is JsonNull }
+ assertTrue { value.jsonPrimitive.isString }
+ assertEquals("null", obj["k"]!!.jsonPrimitive.content)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonReifiedCollectionsTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonReifiedCollectionsTest.kt
new file mode 100644
index 00000000..3fc62489
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonReifiedCollectionsTest.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class JsonReifiedCollectionsTest : JsonTestBase() {
+ @Serializable
+ data class DataHolder(val data: String)
+
+ @Test
+ fun testReifiedList() = parametrizedTest { jsonTestingMode ->
+ val data = listOf(DataHolder("data"), DataHolder("not data"))
+ val json = default.encodeToString(data, jsonTestingMode)
+ val data2 = default.decodeFromString<List<DataHolder>>(json, jsonTestingMode)
+ assertEquals(data, data2)
+ }
+
+ @Test
+ fun testReifiedMap() = parametrizedTest { jsonTestingMode ->
+ val data = mapOf("data" to DataHolder("data"), "smth" to DataHolder("not data"))
+ val json = lenient.encodeToString(data, jsonTestingMode)
+ val data2 = lenient.decodeFromString<Map<String, DataHolder>>(json, jsonTestingMode)
+ assertEquals(data, data2)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonRootLevelNullTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonRootLevelNullTest.kt
new file mode 100644
index 00000000..69397764
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonRootLevelNullTest.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.builtins.*
+import kotlin.test.*
+
+class JsonRootLevelNullTest : JsonTestBase() {
+
+ @Serializable
+ private data class Simple(val a: Int = 42)
+
+ @Test
+ fun testNullableEncode() {
+ // Top-level nulls in tagged encoder is not yet supported, no parametrized test
+ val obj: Simple? = null
+ val json = default.encodeToString(Simple.serializer().nullable, obj)
+ assertEquals("null", json)
+ }
+
+ @Test
+ fun testNullableDecode() = parametrizedTest { jsonTestingMode ->
+ val result = default.decodeFromString(Simple.serializer().nullable, "null", jsonTestingMode)
+ assertNull(result)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonSealedSubclassTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonSealedSubclassTest.kt
new file mode 100644
index 00000000..5fac878d
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonSealedSubclassTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+sealed class Expr
+
+@Serializable
+data class Var(val id: String) : Expr()
+
+class JsonSealedSubclassTest : JsonTestBase() {
+
+ // inspired by kotlinx.serialization/#112
+ @Test
+ fun testCallSuperSealedConstructorProperly() = parametrizedTest { jsonTestingMode ->
+ val v1 = Var("a")
+ val s1 = default.encodeToString(Var.serializer(), v1, jsonTestingMode)// {"id":"a"}
+ assertEquals("""{"id":"a"}""", s1)
+ val v2: Var = default.decodeFromString(Var.serializer(), s1, JsonTestingMode.STREAMING) // should not throw IllegalAccessError
+ assertEquals(v1, v2)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt
new file mode 100644
index 00000000..4b39308d
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.modules.EmptySerializersModule
+import kotlinx.serialization.modules.SerializersModule
+import kotlinx.serialization.test.*
+import kotlin.test.assertEquals
+
+enum class JsonTestingMode {
+ STREAMING,
+ TREE,
+ JAVA_STREAMS;
+
+ companion object {
+ fun value(i: Int) = values()[i]
+ }
+}
+
+abstract class JsonTestBase {
+ protected val default = Json { encodeDefaults = true }
+ protected val lenient = Json { isLenient = true; ignoreUnknownKeys = true; allowSpecialFloatingPointValues = true }
+
+ internal inline fun <reified T : Any> Json.encodeToString(value: T, jsonTestingMode: JsonTestingMode): String {
+ val serializer = serializersModule.serializer<T>()
+ return encodeToString(serializer, value, jsonTestingMode)
+ }
+
+ internal fun <T> Json.encodeToString(
+ serializer: SerializationStrategy<T>,
+ value: T,
+ jsonTestingMode: JsonTestingMode
+ ): String =
+ when (jsonTestingMode) {
+ JsonTestingMode.STREAMING -> {
+ encodeToString(serializer, value)
+ }
+ JsonTestingMode.JAVA_STREAMS -> {
+ encodeViaStream(serializer, value)
+ }
+ JsonTestingMode.TREE -> {
+ val tree = writeJson(value, serializer)
+ encodeToString(tree)
+ }
+ }
+
+ internal inline fun <reified T : Any> Json.decodeFromString(source: String, jsonTestingMode: JsonTestingMode): T {
+ val deserializer = serializersModule.serializer<T>()
+ return decodeFromString(deserializer, source, jsonTestingMode)
+ }
+
+ internal fun <T> Json.decodeFromString(
+ deserializer: DeserializationStrategy<T>,
+ source: String,
+ jsonTestingMode: JsonTestingMode
+ ): T =
+ when (jsonTestingMode) {
+ JsonTestingMode.STREAMING -> {
+ decodeFromString(deserializer, source)
+ }
+ JsonTestingMode.JAVA_STREAMS -> {
+ decodeViaStream(deserializer, source)
+ }
+ JsonTestingMode.TREE -> {
+ val lexer = StringJsonLexer(source)
+ val input = StreamingJsonDecoder(this, WriteMode.OBJ, lexer, deserializer.descriptor)
+ val tree = input.decodeJsonElement()
+ lexer.expectEof()
+ readJson(tree, deserializer)
+ }
+ }
+
+ protected open fun parametrizedTest(test: (JsonTestingMode) -> Unit) {
+ processResults(buildList {
+ add(runCatching { test(JsonTestingMode.STREAMING) })
+ add(runCatching { test(JsonTestingMode.TREE) })
+ if (isJvm()) {
+ add(runCatching { test(JsonTestingMode.JAVA_STREAMS) })
+ }
+ })
+ }
+
+ private inner class SwitchableJson(
+ val json: Json,
+ val jsonTestingMode: JsonTestingMode,
+ override val serializersModule: SerializersModule = EmptySerializersModule
+ ) : StringFormat {
+ override fun <T> encodeToString(serializer: SerializationStrategy<T>, value: T): String {
+ return json.encodeToString(serializer, value, jsonTestingMode)
+ }
+
+ override fun <T> decodeFromString(deserializer: DeserializationStrategy<T>, string: String): T {
+ return json.decodeFromString(deserializer, string, jsonTestingMode)
+ }
+ }
+
+ protected fun parametrizedTest(json: Json, test: StringFormat.() -> Unit) {
+ val streamingResult = runCatching { SwitchableJson(json, JsonTestingMode.STREAMING).test() }
+ val treeResult = runCatching { SwitchableJson(json, JsonTestingMode.TREE).test() }
+ processResults(listOf(streamingResult, treeResult))
+ }
+
+ protected fun processResults(results: List<Result<*>>) {
+ results.forEachIndexed { i, result ->
+ result.onFailure {
+ println("Failed test for ${JsonTestingMode.value(i)}")
+ throw it
+ }
+ }
+ for (i in results.indices) {
+ for (j in results.indices) {
+ if (i == j) continue
+ assertEquals(
+ results[i].getOrNull()!!,
+ results[j].getOrNull()!!,
+ "Results differ for ${JsonTestingMode.value(i)} and ${JsonTestingMode.value(j)}"
+ )
+ }
+ }
+ }
+
+ /**
+ * Same as [assertStringFormAndRestored], but tests both json converters (streaming and tree)
+ * via [parametrizedTest]
+ */
+ internal fun <T> assertJsonFormAndRestored(
+ serializer: KSerializer<T>,
+ data: T,
+ expected: String,
+ json: Json = default
+ ) {
+ parametrizedTest { jsonTestingMode ->
+ val serialized = json.encodeToString(serializer, data, jsonTestingMode)
+ assertEquals(expected, serialized, "Failed with streaming = $jsonTestingMode")
+ val deserialized: T = json.decodeFromString(serializer, serialized, jsonTestingMode)
+ assertEquals(data, deserialized, "Failed with streaming = $jsonTestingMode")
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonTransformingSerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTransformingSerializerTest.kt
new file mode 100644
index 00000000..516587f5
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTransformingSerializerTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlin.test.*
+
+class JsonTransformingSerializerTest : JsonTestBase() {
+ val json = Json { encodeDefaults = false }
+
+ @Serializable
+ data class Example(
+ val name: String,
+ @Serializable(UnwrappingJsonListSerializer::class) val data: StringData,
+ @SerialName("more_data") @Serializable(WrappingJsonListSerializer::class) val moreData: List<StringData> = emptyList()
+ )
+
+ object WrappingJsonListSerializer :
+ JsonTransformingSerializer<List<StringData>>(ListSerializer(StringData.serializer())) {
+ override fun transformDeserialize(element: JsonElement): JsonElement =
+ if (element !is JsonArray) JsonArray(listOf(element)) else element
+ }
+
+ object UnwrappingJsonListSerializer :
+ JsonTransformingSerializer<StringData>(StringData.serializer()) {
+ override fun transformDeserialize(element: JsonElement): JsonElement {
+ if (element !is JsonArray) return element
+ require(element.size == 1) { "Array size must be equal to 1 to unwrap it" }
+ return element.first()
+ }
+ }
+
+ object DroppingNameSerializer : JsonTransformingSerializer<Example>(Example.serializer()) {
+ override fun transformSerialize(element: JsonElement): JsonElement =
+ JsonObject(element.jsonObject.filterNot { (k, v) -> k == "name" && v.jsonPrimitive.content == "First" })
+ }
+
+ @Test
+ fun testExampleCanBeParsed() = parametrizedTest { streaming ->
+ val testDataInput = listOf(
+ """{"name":"test","data":{"data":"str1"},"more_data":[{"data":"str2"}]}""",
+ """{"name":"test","data":{"data":"str1"},"more_data":{"data":"str2"}}""",
+ """{"name":"test","data":[{"data":"str1"}],"more_data":[{"data":"str2"}]}""",
+ """{"name":"test","data":[{"data":"str1"}],"more_data":{"data":"str2"}}"""
+ )
+ val goldenVal = Example("test", StringData("str1"), listOf(StringData("str2")))
+
+
+ for (i in testDataInput.indices) {
+ assertEquals(
+ goldenVal,
+ json.decodeFromString(Example.serializer(), testDataInput[i], streaming),
+ "failed test on ${testDataInput[i]}, jsonTestingMode = $streaming"
+ )
+ }
+ }
+
+ @Test
+ fun testExampleDroppingNameSerializer() = parametrizedTest { streaming ->
+ val testDataInput = listOf(
+ Example("First", StringData("str1")),
+ Example("Second", StringData("str1"))
+ )
+
+ val goldenVals = listOf(
+ """{"data":{"data":"str1"}}""",
+ """{"name":"Second","data":{"data":"str1"}}"""
+ )
+ for (i in testDataInput.indices) {
+ assertEquals(
+ goldenVals[i],
+ json.encodeToString(DroppingNameSerializer, testDataInput[i], streaming),
+ "failed test on ${testDataInput[i]}, jsonTestingMode = $streaming"
+ )
+ }
+ }
+
+ @Serializable
+ data class DocExample(
+ @Serializable(DocJsonListSerializer::class) val data: String
+ )
+
+ object DocJsonListSerializer :
+ JsonTransformingSerializer<String>(serializer()) {
+ override fun transformDeserialize(element: JsonElement): JsonElement {
+ if (element !is JsonArray) return element
+ require(element.size == 1) { "Array size must be equal to 1 to unwrap it" }
+ return element.first()
+ }
+ }
+
+ @Test
+ fun testDocumentationSample() = parametrizedTest { streaming ->
+ val correctExample = DocExample("str1")
+ assertEquals(correctExample, json.decodeFromString(DocExample.serializer(), """{"data":["str1"]}""", streaming))
+ assertEquals(correctExample, json.decodeFromString(DocExample.serializer(), """{"data":"str1"}""", streaming))
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonTransientTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTransientTest.kt
new file mode 100644
index 00000000..c46a11d5
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTransientTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:Suppress("EqualsOrHashCode")
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.Transient
+import kotlinx.serialization.json.internal.*
+import kotlin.test.*
+
+class JsonTransientTest : JsonTestBase() {
+
+ @Serializable
+ class Data(val a: Int = 0, @Transient var b: Int = 42, val e: Boolean = false) {
+ var c = "Hello"
+ val d: String
+ get() = "hello"
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as Data
+
+ if (a != other.a) return false
+ if (b != other.b) return false
+ if (c != other.c) return false
+ if (d != other.d) return false
+
+ return true
+ }
+
+ override fun toString(): String {
+ return "Data(a=$a, b=$b, e=$e, c='$c', d='$d')"
+ }
+ }
+
+ @Test
+ fun testAll() = parametrizedTest { jsonTestingMode ->
+ assertEquals("""{"a":0,"e":false,"c":"Hello"}""",
+ default.encodeToString(Data.serializer(), Data(), jsonTestingMode))
+ }
+
+ @Test
+ fun testMissingOptionals() = parametrizedTest { jsonTestingMode ->
+ assertEquals(default.decodeFromString(Data.serializer(), """{"a":0,"c":"Hello"}""", jsonTestingMode), Data())
+ assertEquals(default.decodeFromString(Data.serializer(), """{"a":0}""", jsonTestingMode), Data())
+ }
+
+ @Test
+ fun testThrowTransient() = parametrizedTest { jsonTestingMode ->
+ assertFailsWith(JsonDecodingException::class) {
+ default.decodeFromString(Data.serializer(), """{"a":0,"b":100500,"c":"Hello"}""", jsonTestingMode)
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeAndMapperTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeAndMapperTest.kt
new file mode 100644
index 00000000..336f630e
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeAndMapperTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlin.test.*
+
+class JsonTreeAndMapperTest {
+ private val decoderData = """{"id":0,"payload":{"from":42,"to":43,"msg":"Hello world"},"timestamp":1000}"""
+ private val decoderError = """{"id":1,"payload":{"error":"Connection timed out"},"timestamp":1001}"""
+
+ @Serializable
+ data class Payload(val from: Long, val to: Long, val msg: String)
+
+ sealed class Either {
+ data class Left(val errorMsg: String) : Either()
+ data class Right(val data: Payload) : Either()
+ }
+
+ object EitherSerializer : KSerializer<Either> {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("Either", PolymorphicKind.SEALED) {
+ val leftDescriptor = buildClassSerialDescriptor("Either.Left") {
+ element<String>("errorMsg")
+ }
+ val rightDescriptor = buildClassSerialDescriptor("Either.Right") {
+ element("data", Payload.serializer().descriptor)
+ }
+ element("left", leftDescriptor)
+ element("right", rightDescriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): Either {
+ val input = decoder as? JsonDecoder ?: throw SerializationException("This class can be loaded only by Json")
+ val tree = input.decodeJsonElement() as? JsonObject
+ ?: throw SerializationException("Expected JsonObject")
+ if ("error" in tree) return Either.Left(tree.getValue("error").jsonPrimitive.content)
+
+ return Either.Right(input.json.decodeFromJsonElement(Payload.serializer(), tree))
+ }
+
+ override fun serialize(encoder: Encoder, value: Either) {
+ val output = encoder as? JsonEncoder ?: throw SerializationException("This class can be saved only by Json")
+ val tree = when (value) {
+ is Either.Left -> JsonObject(mapOf("error" to JsonPrimitive(value.errorMsg)))
+ is Either.Right -> output.json.encodeToJsonElement(Payload.serializer(), value.data)
+ }
+
+ output.encodeJsonElement(tree)
+ }
+ }
+
+ @Serializable
+ data class Event(
+ val id: Int,
+ @Serializable(with = EitherSerializer::class) val payload: Either,
+ val timestamp: Long
+ )
+
+ @Test
+ fun testParseData() {
+ val ev = Json.decodeFromString(Event.serializer(), decoderData)
+ with(ev) {
+ assertEquals(0, id)
+ assertEquals(Either.Right(Payload(42, 43, "Hello world")), payload)
+ assertEquals(1000, timestamp)
+ }
+ }
+
+ @Test
+ fun testParseError() {
+ val ev = Json.decodeFromString(Event.serializer(), decoderError)
+ with(ev) {
+ assertEquals(1, id)
+ assertEquals(Either.Left("Connection timed out"), payload)
+ assertEquals(1001, timestamp)
+ }
+ }
+
+ @Test
+ fun testWriteData() {
+ val encoderData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000)
+ val ev = Json.encodeToString(Event.serializer(), encoderData)
+ assertEquals(decoderData, ev)
+ }
+
+ @Test
+ fun testWriteError() {
+ val encoderError = Event(1, Either.Left("Connection timed out"), 1001)
+ val ev = Json.encodeToString(Event.serializer(), encoderError)
+ assertEquals(decoderError, ev)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeImplicitNullsTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeImplicitNullsTest.kt
new file mode 100644
index 00000000..995459e3
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeImplicitNullsTest.kt
@@ -0,0 +1,14 @@
+package kotlinx.serialization.json
+
+import kotlinx.serialization.KSerializer
+
+class JsonTreeImplicitNullsTest: AbstractJsonImplicitNullsTest() {
+ override fun <T> Json.encode(value: T, serializer: KSerializer<T>): String {
+ return encodeToJsonElement(serializer, value).toString()
+ }
+
+ override fun <T> Json.decode(json: String, serializer: KSerializer<T>): T {
+ val jsonElement = parseToJsonElement(json)
+ return decodeFromJsonElement(serializer, jsonElement)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeTest.kt
new file mode 100644
index 00000000..3506e1f8
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonTreeTest.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+import kotlin.test.assertTrue
+
+class JsonTreeTest : JsonTestBase() {
+ @Serializable
+ data class Data(val a: Int)
+
+ @Serializable
+ data class DataWrapper(val s: String, val d: Data?)
+
+ @Serializable
+ data class DataWrapperOptional(val s: String, val d: Data? = null)
+
+ @Serializable
+ data class IntList(val l: List<Int>)
+
+ @Serializable
+ data class DataList(val l: List<Data>)
+
+ @Serializable
+ data class ListOfLists(val l: List<List<Data>>)
+
+ @Serializable
+ data class MapWrapper(val m: Map<String, Int>)
+
+ @Serializable
+ data class ComplexMapWrapper(val m: Map<String, Data>)
+
+ @Serializable
+ data class AllTypes(
+ val b: Byte,
+ val s: Short,
+ val i: Int,
+ val f: Float,
+ val d: Double,
+ val c: Char,
+ val B: Boolean,
+ val S: String
+ )
+
+ private val json = Json
+ private fun prepare(input: String): JsonElement = lenient.parseToJsonElement(input)
+
+ @Test
+ fun testReadTreeSimple() {
+ val tree = prepare("{a: 42}")
+ val parsed = lenient.decodeFromJsonElement(Data.serializer(), tree)
+ assertEquals(Data(42), parsed)
+ }
+
+ @Test
+ fun testReadTreeNested() {
+ val tree = prepare("""{s:"foo", d:{a:42}}""")
+ val parsed = lenient.decodeFromJsonElement(DataWrapper.serializer(), tree)
+ val expected = DataWrapper("foo", Data(42))
+ assertEquals(expected, parsed)
+ assertEquals(3, parsed.s.length)
+ }
+
+ @Test
+ fun testReadTreeAllTypes() {
+ val tree = prepare("""{ b: 1, s: 2, i: 3, f: 1.0, d: 42.0, c: "a", B: true, S: "str"}""")
+ val kotlinObj = AllTypes(1, 2, 3, 1.0f, 42.0, 'a', true, "str")
+
+ assertEquals(kotlinObj, json.decodeFromJsonElement(AllTypes.serializer(), tree))
+ }
+
+ @Test
+ fun testReadTreeNullable() {
+ val tree1 = prepare("""{s:"foo", d: null}""")
+ val tree2 = prepare("""{s:"foo"}""")
+
+ assertEquals(DataWrapper("foo", null), lenient.decodeFromJsonElement(DataWrapper.serializer(), tree1))
+ assertFailsWithMissingField { lenient.decodeFromJsonElement(DataWrapper.serializer(), tree2) }
+ }
+
+ @Test
+ fun testReadTreeOptional() {
+ val tree1 = prepare("""{s:"foo", d: null}""")
+ val tree2 = prepare("""{s:"foo"}""")
+
+ assertEquals(DataWrapperOptional("foo", null), json.decodeFromJsonElement(DataWrapperOptional.serializer(), tree1))
+ assertEquals(DataWrapperOptional("foo", null), json.decodeFromJsonElement(DataWrapperOptional.serializer(), tree2))
+ }
+
+ @Test
+ fun testReadTreeList() {
+ val tree1 = prepare("""{l:[1,2]}""")
+ val tree2 = prepare("""{l:[{a:42},{a:43}]}""")
+ val tree3 = prepare("""{l:[[],[{a:42}]]}""")
+
+ assertEquals(IntList(listOf(1, 2)), lenient.decodeFromJsonElement(IntList.serializer(), tree1))
+ assertEquals(DataList(listOf(Data(42), Data(43))), lenient.decodeFromJsonElement(DataList.serializer(), tree2))
+ assertEquals(ListOfLists(listOf(listOf(), listOf(Data(42)))), json.decodeFromJsonElement(ListOfLists.serializer(), tree3))
+ }
+
+ @Test
+ fun testReadTreeMap() {
+ val dyn = prepare("{m : {\"a\": 1, \"b\" : 2}}")
+ val m = MapWrapper(mapOf("a" to 1, "b" to 2))
+ assertEquals(m, lenient.decodeFromJsonElement(MapWrapper.serializer(), dyn))
+ }
+
+ @Test
+ fun testReadTreeComplexMap() {
+ val dyn = prepare("{m : {1: {a: 42}, 2: {a: 43}}}")
+ val m = ComplexMapWrapper(mapOf("1" to Data(42), "2" to Data(43)))
+ assertEquals(m, lenient.decodeFromJsonElement(ComplexMapWrapper.serializer(), dyn))
+ }
+
+ private inline fun <reified T: Any> writeAndTest(obj: T, serial: KSerializer<T>, printDiagnostics: Boolean = false): Pair<JsonElement, T> {
+ val tree = lenient.encodeToJsonElement(serial, obj)
+ val str = tree.toString()
+ if (printDiagnostics) println(str)
+ val restored = lenient.decodeFromJsonElement(serial, lenient.parseToJsonElement(str))
+ assertEquals(obj, restored)
+ return tree to restored
+ }
+
+ @Test
+ fun testSaveSimpleNestedTree() {
+ writeAndTest(DataWrapper("foo", Data(42)), DataWrapper.serializer())
+ }
+
+ @Test
+ fun testSaveComplexMapTree() {
+ writeAndTest(ComplexMapWrapper(mapOf("foo" to Data(42), "bar" to Data(43))), ComplexMapWrapper.serializer())
+ }
+
+ @Test
+ fun testSaveNestedLists() {
+ writeAndTest(ListOfLists(listOf(listOf(), listOf(Data(1), Data(2)))), ListOfLists.serializer())
+ }
+
+ @Test
+ fun testSaveOptional() {
+ writeAndTest(DataWrapperOptional("foo", null), DataWrapperOptional.serializer())
+ }
+
+ @Test
+ fun testSaveAllTypes() {
+ writeAndTest(AllTypes(1, -2, 100500, 0.0f, 2048.2, 'a', true, "foobar"), AllTypes.serializer())
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonUnicodeTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonUnicodeTest.kt
new file mode 100644
index 00000000..1f6f814f
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonUnicodeTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.test.*
+import kotlin.random.*
+import kotlin.test.*
+
+class JsonUnicodeTest : JsonTestBase() {
+
+ @Serializable
+ data class UnicodeKeys(
+ @SerialName("\uD83E\uDD14") val thinking: String,
+ @SerialName("🤔?") val thinking2: String,
+ @SerialName("\uD83E\uDD15") val bandage: String,
+ @SerialName("\"") val escaped: String
+ )
+
+ @Test
+ fun testUnicodeKeys() {
+ val data = UnicodeKeys("1", "2", "3", "4")
+ val s = """{"\uD83E\uDD14":"1","\uD83E\uDD14?":"2","\uD83E\uDD15":"3","\"":"4"}"""
+ assertEquals(data, Json.decodeFromString(s))
+ }
+
+ @Test
+ fun testUnicodeValues() {
+ val data = UnicodeKeys(
+ "\uD83E\uDD14", "\" \uD83E\uDD14", "\uD83E\uDD14",
+ "slow-path-in-\"-the-middle\""
+ )
+ assertEquals(data, Json.decodeFromString(Json.encodeToString(data)))
+ }
+
+ @Serializable
+ data class Wrapper(val s: String)
+
+ @Test
+ fun testLongEscapeSequence() {
+ assertSerializedAndRestored(Wrapper("\"".repeat(100)), Wrapper.serializer())
+ // #1456
+ assertSerializedAndRestored(
+ Wrapper("{\"status\":123,\"message\":\"content\",\"path\":\"/here/beeeeeeeeeeee/dragoons/d63574f-705c-49dd-a6bc-c8d1e524eefd/\"}"),
+ Wrapper.serializer()
+ )
+ // #1460
+ assertSerializedAndRestored(
+ """{"avatar_url":"https://cdn.discordapp.com/avatars/384333349063491584/8adca1bddf8c5c46c7deed3edbd80d60.png","embeds":[{"color":1741274,"author":{"icon_url":"https://pbs.twimg.com/profile_images/1381321181719109633/4bpPMaer_normal.jpg","name":"Merlijn replied:","url":"https://twitter.com/@PixelHamster/status/1390719238155952129"},"description":"[@shroomizu](https://twitter.com/shroomizu) time for a pro controller","type":"rich","timestamp":"2021-05-07T17:24:39Z"}],"username":"Merijn"}""",
+ String.serializer()
+ )
+
+ assertSerializedAndRestored(
+ Wrapper("null抢\u000e鋽ìœä‘œåŽ¼\uF70A紲ᢨ䣠null⛾䉻嘖ç·á¯§null쎶\u0005null\u0013\uECC9nullè—œnull㴦铰\\bnull\\f똆\u0010蕧⛺null\u0014毣檚牅äˆnullnullnullnullnullï¿…\uF7E1ì’ªnull퓈nullnullnullnullnull?null\uF7EA釸팔nullè’’\uF840\u0014\u001c\u000få½\u000fnull㩻ㅃ\u0005\u001fnullí’¸\u0011\u0FBDnullnullnull\u0006á¡—æ’º\u000enullnullnullnull\u2DBF\uED3C굃nullÛƒnull䨻醙䗰?\uE5EF\uE656null\uE819?\u0017null㈰nullà£È°\uF485\u0017null浣䃛æžnullnull?â´ƒëŽnull튫nullהꀠnullﲪnull\u000b\u000enull\u001d猶á„nullnullnull鎡nullâ¤nullç뫌ꛖ\\\"nullnull芘䭠\u0006\u0005ቲ慔\uE8A8Ù±ç¹?nullnullnull\\t\u0EFA\uEDC9ê´™?\u0007â \u001c\uF5D9ëž´nullnull\\t묽null甄\uED64䥀null\u0007å©\\b\u0002\\\\nullè‡nullnullnullnullå·“\u000f剛\u0004æ»´\u0010nullnullnullnullnull烌\u0006nullnullí’œnull\uEF0F\u001b\u0005nullé±½åŠnullnullë‹™nullnullnullnullnullnullâ§\u000bnullnull↥\\tæ½éµ€nullnull嘡nullnullê‘\u0007뀨nullnullꑜ䇓亶v鶣\uE4C8\\bnull퇾nullnullÞ•nullâ»—á·null\u001dnullnullnullnull\\b⢅놫賗ᯱ\uF44A\u0016\uF8B7扈\\fnullnull妯nullnullพnull쨆null⤥슅\u0006㥹\u0016null滀nullã¼»\u0007nullæ½ë¿™íˆ›å–®è³ŽçŒ˜nullnull\u0005춎null兀nullnull诃䅤nullnull鵺쒢ꦀnullè«Ÿ\u001c훕nulláŒnull\u000bƒ볤Ëè€\u001enull抅ǩౄ켎nullå¿null\uFDCA\u000b૧奵nullﶼ㣘null\u001c\uF085nullꉫ腈null뛅⚼갩nullnullnullꕂ䦾ë¶\u001e\u2EFF\u0010\uE67Cnull\u0000\u0005null\\\\null䣉\u001b\u0017\u0000\u001fèµ\u0007êž‚é©¿\u0005á¸null臅\u0007nullnullnull\u0003í‚Շ矙\uEB60複null\uF5E1nullí–—ç¶null\\r\u0002\uE76Fꢬnullϗ뺤⫷nullnull⾿null苵nullnull퉰\u000f\\bæ‘®null\u0019利\\b䷈ꡎè’null븘\uF07E\u0015\u0017nullnullä”\u000f\u001a\\t꣪nullླྀ?\\bnullnullnull묣null鎱熅null\\n鳜鱽黦ꘜ뱈\u0EDAnullnullnullnullnullnull뫾바\u0007ê©•í‘Š\u001cnullnull套\u0017null\u0004샂null饘null\uF2EB툛nullnullnull옉\uE2B7null\uED6Fnull\u0004罟null\u0011ËŸ\\fè²null팩\uFD4Anull⥴\uEA9Enullnullâ¡Žæ»ç‹¯nullnullÓ‹null翅ભ\u0007äŸnull\uF043\u0011nullè·©nullnull倿\u09BB賘null\uE514â¾™nullꭜѲం\\\"⎾null땈\uF36E\u0007\uE6D9nullå©£ã­null\uE570nullnullå‚¢nullnull\uF6DB慞嘼null\u187B㺯â¹á˜ƒã›å¹´ì‡¡null讬æžä œæ˜…á¾½null檳\u0010\u0007\\t֊壑ïµè©‡nullnull\u001e\uF3D7\\r妗뙇nullínull\u0003㖣뢠ᮉänull蘜\u000e\u0006nullì¡Žnullnull?庪null䞺黩null\u000e뎤null\u0013null\u001d윸\u20F6ᆵ\uF57B\\\"猬null\u001bnullnullnullnull抙\uEAC3ꢨ\u001b\uF0C6\u0002\uF41Enull\u0014\uF3C5訇깤å€åŒ­?íº\u000eꩦnull\u0004\u0013æ§null眜null\u0015飥nullà°¸nullâ…†nullnull\u0002nullnull\uE442\uF2A0é—›null渜null㊄\u0001≧긷null螥\\\\nullቹÄnull\u0018nullç®±nullnull端\uF7B5â‹’ä‚\u001c饆抋\u001eá³\\rê³µnullnull\u1739null쨒ꭇnull\\f\u0003\u0018null햖㈙\u001enullꃿnullꃦ\u0000null᧡뚦\uAACEnullnullnull\u0019null\u061C㞮췦\u000eàµnull\u0014\u0015nullæ°null\u0018null禟斛♷\uEAB3nullnull\uE82Dç½ê†Ÿnull\\fnullnullլ癱㗋䢵?\u0015\u0005㪙췗null\u0006帡\u0013倫ﴚnullâ«£\u0000鲉null\u001dnull\u0010쓸릌null\u0005⨓nullç–°\u000bå’nullnull\uEAA4ꕸnull塯䩡ì€null?nullnullnullâ—null\u0000\\f\uE3EB悔榴촓ä‰æ¤™nullä“–null\u0005null鉌nullnullnull⦙null\uEA21null\u0011\u001enull\u001enullå²\u001cnullnull좩nulláš«ê›±nullì›ê’’null䨉ãšâ‹Žnull\u0002é•…\uEF30댵\\fnull촴뺴ë¶è‚Žnull鯑詺\uF618nullnullë…½null眴nullnull郱ᘘ斮궡nullnull뛋⋎榩?딡nullnull?\u0014蘉\uF2E1null\\bnullnull추null\u0005ã‰ê¤™ê‡ˆå§±null㪹\u0002ãˆnullnullnull\u001f㇇\u0017蒷墛nullÉ©\\\"null\u000f⬆襤nullnull┭ഀ溜ࢳâ§äº null컈\u0019\\tnullnullﳎnull\u0007null\\t⧘\u0014nullﲘnullë£nullᯜ㧭푬쓉null\u001anull〪䣩䃂ﺤ찅nullæ—â—ä…³nullâ­®nullnull\u0016nullnullnull\uE825郞㬃嶘null\\nê¼»\uF08Enull\uE4D7⒙䑮null\uE0E2nullnull䢱null\u0003\uEBFB苚釳Ǧ\u0012\u000b\u0011nullnull\\bnullà´?\u000fﵕ鋨짰\u0001null憞\u001b๘null祆\u001cnull॒꺰\u0010Ñ»null?\u000b˻갇nullã¿\uE63Cã–\u0016匃\\n\u0001null\\rnullnullá…‹null\uE365↾抬ê nullã¾á•ší—•ì±±\u0002\uEDB5\u0010null凵\u1ADA༽Ꙋ긚nullᵪnull\\bnullï¾³í¶null얄팼ަᔄ\uF120nullá¬\\râ­null퀉蟸焋\\fnullâ®nullnull?\u0012\u0017ê°ž?á¥null┌\\\"\uE803nullnullnullɂ财⎱nullì—null娮nullnullnullê›°nullå£\u0019ヷ맾á£\uE766ï°Ÿnullnullnullnullç¥null\u0000null\u0C84뽉툑null í윲\u001d愼ᱳnullê®null㕃nullê°€nullã—±nullצnull\u0001null춊\u001fíž¾\u001a\u0016┒옎璻电絒\u0015\u001bá‘null\u0006\u0006\u0002㳟nullnullÔˆnull\u1F5Anullnullnull\u000fnullnullnullЧ曗კ\uF5A8null錦Ӣnull\u000b"),
+ Wrapper.serializer()
+ )
+ }
+
+ @Test
+ fun testRandomEscapeSequences() = noJs { // Too slow on JS
+ repeat(10_000) {
+ val s = generateRandomUnicodeString(Random.nextInt(1, 2047))
+ try {
+ assertSerializedAndRestored(s, String.serializer())
+ } catch (e: Throwable) {
+ // Not assertion error to preserve cause
+ throw IllegalStateException("Unexpectedly failed test, cause string: $s", e)
+ }
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonUnionEnumTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonUnionEnumTest.kt
new file mode 100644
index 00000000..76634bbc
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonUnionEnumTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class JsonUnionEnumTest : JsonTestBase() {
+
+ enum class SomeEnum { ALPHA, BETA, GAMMA }
+
+ @Serializable
+ data class WithUnions(val s: String,
+ val e: SomeEnum = SomeEnum.ALPHA,
+ val i: Int = 42)
+
+ @Test
+ fun testEnum() = parametrizedTest { jsonTestingMode ->
+ val data = WithUnions("foo", SomeEnum.BETA)
+ val json = default.encodeToString(WithUnions.serializer(), data, jsonTestingMode)
+ val restored = default.decodeFromString(WithUnions.serializer(), json, jsonTestingMode)
+ assertEquals(data, restored)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonUpdateModeTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonUpdateModeTest.kt
new file mode 100644
index 00000000..eccef39d
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonUpdateModeTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class JsonOverwriteTest : JsonTestBase() {
+ @Serializable
+ data class Updatable1(val l: List<Int>)
+
+ @Serializable
+ data class Data(val a: Int)
+
+ @Serializable
+ data class Updatable2(val l: List<Data>)
+
+ @Serializable
+ data class NullableInnerIntList(val data: List<Int?>)
+
+ @Serializable
+ data class NullableUpdatable(val data: List<Data>?)
+
+ @Test
+ fun testCanUpdatePrimitiveList() = parametrizedTest { jsonTestingMode ->
+ val parsed =
+ lenient.decodeFromString<Updatable1>(Updatable1.serializer(), """{"l":[1,2],"f":"foo","l":[3,4]}""", jsonTestingMode)
+ assertEquals(Updatable1(listOf(3, 4)), parsed)
+ }
+
+ @Test
+ fun testCanUpdateObjectList() = parametrizedTest { jsonTestingMode ->
+ val parsed = lenient.decodeFromString<Updatable2>(
+ Updatable2.serializer(),
+ """{"f":"bar","l":[{"a":42}],"l":[{"a":43}]}""",
+ jsonTestingMode
+ )
+ assertEquals(Updatable2(listOf(Data(43))), parsed)
+ }
+
+ @Test
+ fun testCanUpdateNullableValuesInside() = parametrizedTest { jsonTestingMode ->
+ val a1 = default.decodeFromString(NullableInnerIntList.serializer(), """{"data":[null],"data":[1]}""", jsonTestingMode)
+ assertEquals(NullableInnerIntList(listOf(1)), a1)
+ val a2 = default.decodeFromString(NullableInnerIntList.serializer(), """{"data":[42],"data":[null]}""", jsonTestingMode)
+ assertEquals(NullableInnerIntList(listOf(null)), a2)
+ val a3 = default.decodeFromString(NullableInnerIntList.serializer(), """{"data":[31],"data":[1]}""", jsonTestingMode)
+ assertEquals(NullableInnerIntList(listOf(1)), a3)
+ }
+
+ @Test
+ fun testCanUpdateNullableValues() = parametrizedTest { jsonTestingMode ->
+ val a1 = lenient.decodeFromString(NullableUpdatable.serializer(), """{"data":null,"data":[{"a":42}]}""", jsonTestingMode)
+ assertEquals(NullableUpdatable(listOf(Data(42))), a1)
+ val a2 = lenient.decodeFromString(NullableUpdatable.serializer(), """{"data":[{a:42}],"data":null}""", jsonTestingMode)
+ assertEquals(NullableUpdatable(null), a2)
+ val a3 = lenient.decodeFromString(NullableUpdatable.serializer(), """{"data":[{a:42}],"data":[{"a":43}]}""", jsonTestingMode)
+ assertEquals(NullableUpdatable(listOf(Data(43))), a3)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/LenientTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/LenientTest.kt
new file mode 100644
index 00000000..b89e853f
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/LenientTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.internal.*
+import kotlin.test.*
+
+class LenientTest : JsonTestBase() {
+
+ @Serializable
+ data class Holder(val i: Int, val l: Long, val b: Boolean, val s: String)
+ val value = Holder(1, 2, true, "string")
+
+ @Serializable
+ data class ListHolder(val l: List<String>)
+ private val listValue = ListHolder(listOf("1", "2", "ss"))
+
+ @Test
+ fun testQuotedInt() = parametrizedTest {
+ val json = """{"i":"1", "l":2, "b":true, "s":"string"}"""
+ assertEquals(value, default.decodeFromString(Holder.serializer(), json, it))
+ assertEquals(value, lenient.decodeFromString(Holder.serializer(), json, it))
+ }
+
+ @Test
+ fun testQuotedLong() = parametrizedTest {
+ val json = """{"i":1, "l":"2", "b":true, "s":"string"}"""
+ assertEquals(value, default.decodeFromString(Holder.serializer(), json, it))
+ assertEquals(value, lenient.decodeFromString(Holder.serializer(), json, it))
+ }
+
+ @Test
+ fun testQuotedBoolean() = parametrizedTest {
+ val json = """{"i":1, "l":2, "b":"true", "s":"string"}"""
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(Holder.serializer(), json, it) }
+ assertEquals(value, lenient.decodeFromString(Holder.serializer(), json, it))
+ }
+
+ @Test
+ fun testUnquotedStringValue() = parametrizedTest {
+ val json = """{"i":1, "l":2, "b":true, "s":string}"""
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(Holder.serializer(), json, it) }
+ assertEquals(value, lenient.decodeFromString(Holder.serializer(), json, it))
+ }
+
+ @Test
+ fun testUnquotedKey() = parametrizedTest {
+ val json = """{"i":1, "l":2, b:true, "s":"string"}"""
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(Holder.serializer(), json, it) }
+ assertEquals(value, lenient.decodeFromString(Holder.serializer(), json, it))
+ }
+
+ @Test
+ fun testUnquotedStringInArray() = parametrizedTest {
+ val json = """{"l":[1, 2, ss]}"""
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(ListHolder.serializer(), json, it) }
+ assertEquals(listValue, lenient.decodeFromString(ListHolder.serializer(), json, it))
+ }
+
+ @Serializable
+ data class StringWrapper(val s: String)
+
+ @Test
+ fun testNullsProhibited() = parametrizedTest {
+ assertEquals(StringWrapper("nul"), lenient.decodeFromString("""{"s":nul}""", it))
+ assertEquals(StringWrapper("null1"), lenient.decodeFromString("""{"s":null1}""", it))
+ assertFailsWith<JsonException> { lenient.decodeFromString<StringWrapper>("""{"s":null}""", it) }
+ }
+
+ @Serializable
+ data class NullableString(val s: String?)
+
+ @Test
+ fun testNullsAllowed() = parametrizedTest {
+ assertEquals(NullableString("nul"), lenient.decodeFromString("""{"s":nul}""", it))
+ assertEquals(NullableString("null1"), lenient.decodeFromString("""{"s":null1}""", it))
+ assertEquals(NullableString(null), lenient.decodeFromString("""{"s":null}""", it))
+ assertEquals(NullableString("null"), lenient.decodeFromString("""{"s":"null"}""", it))
+ assertEquals(NullableString("null"), lenient.decodeFromString("""{"s":"null" }""", it))
+ assertEquals(NullableString("null "), lenient.decodeFromString("""{"s":"null " }""", it))
+ }
+
+ @Test
+ fun testTopLevelNulls() = parametrizedTest {
+ assertEquals("nul", lenient.decodeFromString("""nul""", it))
+ assertEquals("null1", lenient.decodeFromString("""null1""", it))
+ assertEquals(null, lenient.decodeFromString(String.serializer().nullable, """null""", it))
+ assertEquals("null", lenient.decodeFromString(""""null"""", it))
+ assertEquals("null ", lenient.decodeFromString(""""null """", it))
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/MapLikeSerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/MapLikeSerializerTest.kt
new file mode 100644
index 00000000..37abd954
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/MapLikeSerializerTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlin.test.*
+
+class MapLikeSerializerTest : JsonTestBase() {
+
+ @Serializable
+ data class StringPair(val a: String, val b: String)
+
+ @Serializer(forClass = StringPair::class)
+ object StringPairSerializer : KSerializer<StringPair> {
+
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("package.StringPair", StructureKind.MAP) {
+ element<String>("a")
+ element<String>("b")
+ }
+
+ override fun serialize(encoder: Encoder, value: StringPair) {
+ val structuredEncoder = encoder.beginStructure(descriptor)
+ structuredEncoder.encodeSerializableElement(descriptor, 0, String.serializer(), value.a)
+ structuredEncoder.encodeSerializableElement(descriptor, 1, String.serializer(), value.b)
+ structuredEncoder.endStructure(descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): StringPair {
+ val composite = decoder.beginStructure(descriptor)
+ if (composite.decodeSequentially()) {
+ val key = composite.decodeSerializableElement(descriptor, 0, String.serializer())
+ val value = composite.decodeSerializableElement(descriptor, 1, String.serializer())
+ return StringPair(key, value)
+ }
+
+ var key: String? = null
+ var value: String? = null
+ mainLoop@ while (true) {
+ when (val idx = composite.decodeElementIndex(descriptor)) {
+ CompositeDecoder.DECODE_DONE -> {
+ break@mainLoop
+ }
+ 0 -> {
+ key = composite.decodeSerializableElement(descriptor, 0, String.serializer())
+ }
+ 1 -> {
+ value = composite.decodeSerializableElement(descriptor, 1, String.serializer())
+ }
+ else -> throw SerializationException("Invalid index: $idx")
+ }
+ }
+ composite.endStructure(descriptor)
+ if (key == null) throw SerializationException("Element 'a' is missing")
+ if (value == null) throw SerializationException("Element 'b' is missing")
+ @Suppress("UNCHECKED_CAST")
+ return StringPair(key, value)
+ }
+ }
+
+ @Test
+ fun testStringPair() = assertJsonFormAndRestored(StringPairSerializer, StringPair("a", "b"), """{"a":"b"}""")
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/SpecialFloatingPointValuesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/SpecialFloatingPointValuesTest.kt
new file mode 100644
index 00000000..745b0747
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/SpecialFloatingPointValuesTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class SpecialFloatingPointValuesTest : JsonTestBase() {
+
+ @Serializable
+ data class Box(val d: Double, val f: Float) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+ other as Box
+ if (d != other.d && !(d.isNaN() && other.d.isNaN())) return false
+ if (f != other.f && !(f.isNaN() && other.f.isNaN())) return false
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = d.hashCode()
+ result = 31 * result + f.hashCode()
+ return result
+ }
+ }
+
+ val json = Json { allowSpecialFloatingPointValues = true }
+
+ @Test
+ fun testNans() = parametrizedTest {
+ test(Box(Double.NaN, Float.NaN), """{"d":NaN,"f":NaN}""", it)
+ noJs { // Number formatting
+ test(Box(0.0, Float.NaN), """{"d":0.0,"f":NaN}""", it)
+ test(Box(Double.NaN, 0.0f), """{"d":NaN,"f":0.0}""", it)
+ }
+ }
+
+ @Test
+ fun testInfinities() = parametrizedTest {
+ test(Box(Double.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY), """{"d":-Infinity,"f":Infinity}""", it)
+ test(Box(Double.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY), """{"d":Infinity,"f":-Infinity}""", it)
+ }
+
+ private fun test(box: Box, expected: String, jsonTestingMode: JsonTestingMode) {
+ val e1 = assertFailsWith<JsonException> { default.encodeToString(Box.serializer(), box, jsonTestingMode) }
+ assertTrue { e1.message!!.contains("Unexpected special floating-point value") }
+ assertEquals(expected, json.encodeToString(Box.serializer(), box, jsonTestingMode))
+ assertEquals(box, json.decodeFromString(Box.serializer(), expected, jsonTestingMode))
+ val e2 = assertFailsWith<JsonException> { default.decodeFromString(Box.serializer(), expected, jsonTestingMode) }
+ assertTrue { e2.message!!.contains("Unexpected special floating-point value") }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonContentPolymorphicSerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonContentPolymorphicSerializerTest.kt
new file mode 100644
index 00000000..d58e26b6
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonContentPolymorphicSerializerTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+class JsonContentPolymorphicSerializerTest : JsonTestBase() {
+ val json = Json
+
+ @Serializable
+ sealed class Choices {
+ @Serializable
+ data class HasA(val a: String) : Choices()
+
+ @Serializable
+ data class HasB(val b: Int) : Choices()
+
+ @Serializable
+ data class HasC(val c: Boolean) : Choices()
+ }
+
+ object ChoicesParametricSerializer : JsonContentPolymorphicSerializer<Choices>(Choices::class) {
+ override fun selectDeserializer(element: JsonElement): KSerializer<out Choices> {
+ val obj = element.jsonObject
+ return when {
+ "a" in obj -> Choices.HasA.serializer()
+ "b" in obj -> Choices.HasB.serializer()
+ "c" in obj -> Choices.HasC.serializer()
+ else -> throw SerializationException("Unknown choice")
+ }
+ }
+ }
+
+ @Serializable
+ data class WithChoices(@Serializable(ChoicesParametricSerializer::class) val response: Choices)
+
+ private val testDataInput = listOf(
+ """{"response":{"a":"string"}}""",
+ """{"response":{"b":42}}""",
+ """{"response":{"c":true}}"""
+ )
+
+ private val testDataOutput = listOf(
+ WithChoices(Choices.HasA("string")),
+ WithChoices(Choices.HasB(42)),
+ WithChoices(Choices.HasC(true))
+ )
+
+ @Test
+ fun testParsesParametrically() = parametrizedTest { streaming ->
+ for (i in testDataInput.indices) {
+ assertEquals(
+ testDataOutput[i],
+ json.decodeFromString(WithChoices.serializer(), testDataInput[i], streaming),
+ "failed test on ${testDataInput[i]}, jsonTestingMode = $streaming"
+ )
+ }
+ }
+
+ @Test
+ fun testSerializesParametrically() = parametrizedTest { streaming ->
+ for (i in testDataOutput.indices) {
+ assertEquals(
+ testDataInput[i],
+ json.encodeToString(WithChoices.serializer(), testDataOutput[i], streaming),
+ "failed test on ${testDataOutput[i]}, jsonTestingMode = $streaming"
+ )
+ }
+ }
+
+ interface Payment {
+ val amount: String
+ }
+
+ @Serializable
+ data class SuccessfulPayment(override val amount: String, val date: String) : Payment
+
+ @Serializable
+ data class RefundedPayment(override val amount: String, val date: String, val reason: String) : Payment
+
+ object PaymentSerializer : JsonContentPolymorphicSerializer<Payment>(Payment::class) {
+ override fun selectDeserializer(element: JsonElement) = when {
+ "reason" in element.jsonObject -> RefundedPayment.serializer()
+ else -> SuccessfulPayment.serializer()
+ }
+ }
+
+ @Test
+ fun testDocumentationSample() = parametrizedTest { streaming ->
+ assertEquals(
+ SuccessfulPayment("1.0", "03.02.2020"),
+ json.decodeFromString(PaymentSerializer, """{"amount":"1.0","date":"03.02.2020"}""", streaming)
+ )
+ assertEquals(
+ RefundedPayment("2.0", "03.02.2020", "complaint"),
+ json.decodeFromString(PaymentSerializer, """{"amount":"2.0","date":"03.02.2020","reason":"complaint"}""", streaming)
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonDeserializePolymorphicTwiceTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonDeserializePolymorphicTwiceTest.kt
new file mode 100644
index 00000000..f0229046
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonDeserializePolymorphicTwiceTest.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+class JsonDeserializePolymorphicTwiceTest {
+
+ @Serializable
+ sealed class Foo {
+ @Serializable
+ data class Bar(val a: Int) : Foo()
+ }
+
+ @Test
+ fun testDeserializeTwice() { // #812
+ val json = Json.encodeToJsonElement(Foo.serializer(), Foo.Bar(1))
+ assertEquals(Foo.Bar(1), Json.decodeFromJsonElement(Foo.serializer(), json))
+ assertEquals(Foo.Bar(1), Json.decodeFromJsonElement(Foo.serializer(), json))
+ }
+} \ No newline at end of file
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonListPolymorphismTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonListPolymorphismTest.kt
new file mode 100644
index 00000000..5722e8df
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonListPolymorphismTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.Polymorphic
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.JsonTestBase
+import kotlin.test.Test
+import kotlin.test.assertFails
+
+class JsonListPolymorphismTest : JsonTestBase() {
+
+ @Serializable
+ internal data class ListWrapper(val list: List<@Polymorphic InnerBase>)
+
+ @Test
+ fun testPolymorphicValues() = assertJsonFormAndRestored(
+ ListWrapper.serializer(),
+ ListWrapper(listOf(InnerImpl(1), InnerImpl2(2))),
+ """{"list":[""" +
+ """{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null},""" +
+ """{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":2}]}""",
+ polymorphicRelaxedJson)
+
+ @Serializable
+ internal data class ListNullableWrapper(val list: List<@Polymorphic InnerBase?>)
+
+ @Test
+ fun testPolymorphicNullableValues() = assertJsonFormAndRestored(
+ ListNullableWrapper.serializer(),
+ ListNullableWrapper(listOf(InnerImpl(1), null)),
+ """{"list":[""" +
+ """{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null},""" +
+ "null]}",
+ polymorphicRelaxedJson)
+
+ @Test
+ fun testPolymorphicNullableValuesWithNonNullSerializerFails() =
+ parametrizedTest { jsonTestingMode ->
+ val wrapper = ListNullableWrapper(listOf(InnerImpl(1), null))
+ val serialized = polymorphicRelaxedJson.encodeToString(ListNullableWrapper.serializer(), wrapper, jsonTestingMode)
+ assertFails { polymorphicRelaxedJson.decodeFromString(ListWrapper.serializer(), serialized, jsonTestingMode) }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonMapPolymorphismTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonMapPolymorphismTest.kt
new file mode 100644
index 00000000..b2adaa71
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonMapPolymorphismTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class JsonMapPolymorphismTest : JsonTestBase() {
+
+ @Serializable
+ internal data class MapWrapper(val map: Map<String, @Polymorphic InnerBase>)
+
+ @Test
+ fun testPolymorphicValues() = assertJsonFormAndRestored(
+ MapWrapper.serializer(),
+ MapWrapper(mapOf("k1" to InnerImpl(1), "k2" to InnerImpl2(2))),
+ """{"map":{"k1":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null},"k2":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":2}}}""".trimMargin(),
+ polymorphicJson
+ )
+
+ @Serializable
+ internal data class MapNullableWrapper(val map: Map<String, @Polymorphic InnerBase?>)
+
+ @Serializable
+ internal data class MapKeys(val map: Map<@Polymorphic InnerBase, String>)
+
+ @Test
+ fun testPolymorphicNullableValues() = assertJsonFormAndRestored(
+ MapNullableWrapper.serializer(),
+ MapNullableWrapper(mapOf("k1" to InnerImpl(1), "k2" to null)),
+ """{"map":{"k1":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null},"k2":null}}""",
+ polymorphicJson
+ )
+
+ @Test
+ fun testPolymorphicKeys() {
+ val json = Json {
+ allowStructuredMapKeys = true
+ serializersModule = polymorphicTestModule
+ encodeDefaults = true
+ }
+ assertJsonFormAndRestored(
+ MapKeys.serializer(),
+ MapKeys(mapOf(InnerImpl(1) to "k2", InnerImpl2(2) to "k2")),
+ """{"map":[{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null},"k2",{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":2},"k2"]}""",
+ json
+ )
+ }
+
+ @Test
+ fun testPolymorphicKeysInArray() {
+ val json = Json {
+ allowStructuredMapKeys = true
+ useArrayPolymorphism = true
+ serializersModule = polymorphicTestModule
+ encodeDefaults = true
+ }
+ assertJsonFormAndRestored(
+ MapKeys.serializer(),
+ MapKeys(mapOf(InnerImpl(1) to "k2", InnerImpl2(2) to "k2")),
+ """{"map":[["kotlinx.serialization.json.polymorphic.InnerImpl",{"field":1,"str":"default","nullable":null}],"k2",["kotlinx.serialization.json.polymorphic.InnerImpl2",{"field":2}],"k2"]}""",
+ json
+ )
+ }
+
+ @Serializable
+ abstract class Base
+
+ @Serializable
+ data class Derived(val myMap: Map<StringData, String>) : Base()
+
+ @Test
+ fun testIssue480() {
+ val json = Json {
+ allowStructuredMapKeys = true
+ serializersModule = SerializersModule {
+ polymorphic(Base::class) {
+ subclass(Derived.serializer())
+ }
+ }
+ }
+
+ assertJsonFormAndRestored(
+ Base.serializer(),
+ Derived(mapOf(StringData("hi") to "hello")),
+ """{"type":"kotlinx.serialization.json.polymorphic.JsonMapPolymorphismTest.Derived","myMap":[{"data":"hi"},"hello"]}""",
+ json
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNestedPolymorphismTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNestedPolymorphismTest.kt
new file mode 100644
index 00000000..0caa99dd
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNestedPolymorphismTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class JsonNestedPolymorphismTest : JsonTestBase() {
+
+ private val polymorphicJson = Json {
+ isLenient = true
+ encodeDefaults = true
+ serializersModule = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(InnerImpl.serializer())
+ subclass(InnerImpl2.serializer())
+ subclass(OuterImpl.serializer())
+
+ }
+
+ polymorphic(InnerBase::class) {
+ subclass(InnerImpl.serializer())
+ subclass(InnerImpl2.serializer())
+ }
+ }
+ }
+
+ @Serializable
+ internal data class NestedGenericsList(val list: List<List<@Polymorphic Any>>)
+
+ @Test
+ fun testAnyList() = assertJsonFormAndRestored(
+ NestedGenericsList.serializer(),
+ NestedGenericsList(listOf(listOf(InnerImpl(1)), listOf(InnerImpl(2)))),
+ """{"list":[[""" +
+ """{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null}],[""" +
+ """{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":2,"str":"default","nullable":null}]]}""",
+ polymorphicJson)
+
+ @Serializable
+ internal data class NestedGenericsMap(val list: Map<String, Map<String, @Polymorphic Any>>)
+
+ @Test
+ fun testAnyMap() = assertJsonFormAndRestored(
+ NestedGenericsMap.serializer(),
+ NestedGenericsMap(mapOf("k1" to mapOf("k1" to InnerImpl(1)))),
+ """{"list":{"k1":{"k1":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl",""" +
+ """"field":1,"str":"default","nullable":null}}}}""",
+ polymorphicJson)
+
+ @Serializable
+ internal data class AnyWrapper(@Polymorphic val any: Any)
+
+ @Test
+ fun testAny() = assertJsonFormAndRestored(
+ AnyWrapper.serializer(),
+ AnyWrapper(OuterImpl(InnerImpl2(1), InnerImpl(2))),
+ """{"any":""" +
+ """{"type":"kotlinx.serialization.json.polymorphic.OuterImpl",""" +
+ """"base":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":1},""" +
+ """"base2":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":2,"str":"default","nullable":null}}}""",
+ polymorphicJson)
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNullablePolymorphicTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNullablePolymorphicTest.kt
new file mode 100644
index 00000000..ba8d0dfe
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNullablePolymorphicTest.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class JsonNullablePolymorphicTest : JsonTestBase() {
+ @Serializable
+ data class NullableHolder(@Polymorphic val a: Any?)
+
+ @Serializable
+ @SerialName("Box")
+ data class Box(val i: Int)
+
+ @Test
+ fun testPolymorphicNulls() {
+ val json = Json {
+ serializersModule = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(Box::class)
+ }
+ }
+ }
+
+ assertJsonFormAndRestored(serializer(), NullableHolder(Box(42)), """{"a":{"type":"Box","i":42}}""", json)
+ assertJsonFormAndRestored(serializer(), NullableHolder(null), """{"a":null}""", json)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicClassDescriptorTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicClassDescriptorTest.kt
new file mode 100644
index 00000000..b11e9dad
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicClassDescriptorTest.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+class JsonPolymorphicClassDescriptorTest : JsonTestBase() {
+
+ private val json = Json {
+ classDiscriminator = "class"
+ serializersModule = polymorphicTestModule
+ encodeDefaults = true
+ }
+
+ @Test
+ fun testPolymorphicProperties() = assertJsonFormAndRestored(
+ InnerBox.serializer(),
+ InnerBox(InnerImpl(42, "foo")),
+ """{"base":{"class":"kotlinx.serialization.json.polymorphic.InnerImpl",""" +
+ """"field":42,"str":"foo","nullable":null}}""",
+ json
+ )
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicObjectTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicObjectTest.kt
new file mode 100644
index 00000000..e47f5790
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicObjectTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class JsonPolymorphicObjectTest : JsonTestBase() {
+
+ @Serializable
+ data class Holder(@Polymorphic val a: Any)
+
+ @Serializable
+ @SerialName("MyObject")
+ object MyObject {
+ @Suppress("unused")
+ val unused = 42
+ }
+
+ val json = Json {
+ serializersModule = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(MyObject::class, MyObject.serializer()) // JS bug workaround
+ }
+ }
+ }
+
+ @Test
+ fun testRegularPolymorphism() {
+ assertJsonFormAndRestored(Holder.serializer(), Holder(MyObject), """{"a":{"type":"MyObject"}}""", json)
+ }
+
+ @Test
+ fun testArrayPolymorphism() {
+ val json = Json(from = json) {
+ useArrayPolymorphism = true
+ }
+ assertJsonFormAndRestored(Holder.serializer(), Holder(MyObject), """{"a":["MyObject",{}]}""", json)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt
new file mode 100644
index 00000000..13e92316
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class JsonPolymorphismExceptionTest : JsonTestBase() {
+
+ @Serializable
+ abstract class Base
+
+ @Serializable
+ @SerialName("derived")
+ class Derived(val nested: Nested = Nested()) : Base()
+
+ @Serializable
+ class Nested
+
+ @Test
+ fun testDecodingException() = parametrizedTest { jsonTestingMode ->
+ val serialModule = SerializersModule {
+ polymorphic(Base::class) {
+ subclass(Derived::class)
+ }
+ }
+
+ assertFailsWith<JsonDecodingException> {
+ Json { serializersModule = serialModule }.decodeFromString(Base.serializer(), """{"type":"derived","nested":null}""", jsonTestingMode)
+ }
+ }
+
+ @Test
+ fun testMissingDiscriminator() = parametrizedTest { jsonTestingMode ->
+ val serialModule = SerializersModule {
+ polymorphic(Base::class) {
+ subclass(Derived::class)
+ }
+ }
+
+ assertFailsWith<JsonDecodingException> {
+ Json { serializersModule = serialModule }.decodeFromString(Base.serializer(), """{"nested":{}}""", jsonTestingMode)
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonProhibitedPolymorphicKindsTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonProhibitedPolymorphicKindsTest.kt
new file mode 100644
index 00000000..7a825395
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonProhibitedPolymorphicKindsTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class JsonProhibitedPolymorphicKindsTest : JsonTestBase() {
+
+ @Serializable
+ sealed class Base {
+ @Serializable
+ class Impl(val data: Int) : Base()
+ }
+
+ @Serializable
+ enum class MyEnum
+
+ @Test
+ fun testSealedSubclass() {
+ assertFailsWith<IllegalArgumentException> {
+ Json(true) {
+ subclass(Base::class)
+ }
+ }
+ assertFailsWith<IllegalArgumentException> {
+ Json(false) {
+ subclass(Base::class)
+ }
+ }
+ }
+
+ @Test
+ fun testPrimitive() {
+ assertFailsWith<IllegalArgumentException> {
+ Json(false) {
+ subclass(Int::class)
+ }
+ }
+
+ // Doesn't fail
+ Json(true) {
+ subclass(Int::class)
+ }
+ }
+
+ @Test
+ fun testEnum() {
+ assertFailsWith<IllegalArgumentException> {
+ Json(false) {
+ subclass(MyEnum::class)
+ }
+ }
+
+ Json(true) {
+ subclass(MyEnum::class)
+ }
+ }
+
+ @Test
+ fun testStructures() {
+ assertFailsWith<IllegalArgumentException> {
+ Json(false) {
+ subclass(serializer<Map<Int, Int>>())
+ }
+ }
+
+ assertFailsWith<IllegalArgumentException> {
+ Json(false) {
+ subclass(serializer<List<Int>>())
+ }
+ }
+
+ Json(true) {
+ subclass(serializer<List<Int>>())
+ }
+
+
+ Json(true) {
+ subclass(serializer<Map<Int, Int>>())
+ }
+ }
+
+ private fun Json(useArrayPolymorphism: Boolean, builderAction: PolymorphicModuleBuilder<Any>.() -> Unit) = Json {
+ this.useArrayPolymorphism = useArrayPolymorphism
+ serializersModule = SerializersModule {
+ polymorphic(Any::class) {
+ builderAction()
+ }
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPropertyPolymorphicTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPropertyPolymorphicTest.kt
new file mode 100644
index 00000000..e2e10e24
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPropertyPolymorphicTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.PolymorphicSerializer
+import kotlinx.serialization.json.JsonTestBase
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class JsonPropertyPolymorphicTest : JsonTestBase() {
+
+ @Test
+ fun testPolymorphicProperties() = assertJsonFormAndRestored(
+ InnerBox.serializer(),
+ InnerBox(InnerImpl(42, "foo")),
+ """{"base":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl",""" +
+ """"field":42,"str":"foo","nullable":null}}""",
+ polymorphicRelaxedJson)
+
+ @Test
+ fun testFlatPolymorphic() = parametrizedTest { jsonTestingMode ->
+ val base: InnerBase = InnerImpl(42, "foo")
+ val string = polymorphicRelaxedJson.encodeToString(PolymorphicSerializer(InnerBase::class), base, jsonTestingMode)
+ assertEquals("""{"type":"kotlinx.serialization.json.polymorphic.InnerImpl",""" +
+ """"field":42,"str":"foo","nullable":null}""", string)
+ assertEquals(base, polymorphicRelaxedJson.decodeFromString(PolymorphicSerializer(InnerBase::class), string, jsonTestingMode))
+ }
+
+ @Test
+ fun testNestedPolymorphicProperties() = assertJsonFormAndRestored(
+ OuterBox.serializer(),
+ OuterBox(OuterImpl(InnerImpl(42), InnerImpl2(42)), InnerImpl2(239)),
+ """{"outerBase":{""" +
+ """"type":"kotlinx.serialization.json.polymorphic.OuterImpl",""" +
+ """"base":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":42,"str":"default","nullable":null},""" +
+ """"base2":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":42}},""" +
+ """"innerBase":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":239}}""",
+ polymorphicRelaxedJson)
+
+ @Test
+ fun testPolymorphicNullableProperties() = assertJsonFormAndRestored(
+ InnerNullableBox.serializer(),
+ InnerNullableBox(InnerImpl(42, "foo")),
+ """{"base":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl",""" +
+ """"field":42,"str":"foo","nullable":null}}""",
+ polymorphicRelaxedJson)
+
+ @Test
+ fun testPolymorphicNullablePropertiesWithNull() =
+ assertJsonFormAndRestored(InnerNullableBox.serializer(), InnerNullableBox(null), """{"base":null}""", polymorphicJson)
+
+ @Test
+ fun testNestedPolymorphicNullableProperties() = assertJsonFormAndRestored(
+ OuterNullableBox.serializer(),
+ OuterNullableBox(OuterNullableImpl(InnerImpl(42), null), InnerImpl2(239)),
+ """{"outerBase":{""" +
+ """"type":"kotlinx.serialization.json.polymorphic.OuterNullableImpl",""" +
+ """"base":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":42,"str":"default","nullable":null},"base2":null},""" +
+ """"innerBase":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":239}}""",
+ polymorphicRelaxedJson)
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt
new file mode 100644
index 00000000..e46de17a
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.native.concurrent.*
+
+@Serializable
+internal open class InnerBase
+
+internal interface OuterBase
+
+@Serializable
+internal data class InnerImpl(val field: Int, val str: String = "default", val nullable: Int? = null) : InnerBase()
+
+@Serializable
+internal data class InnerImpl2(val field: Int) : InnerBase()
+
+@Serializable
+internal data class InnerBox(@Polymorphic val base: InnerBase)
+
+@Serializable
+internal data class InnerNullableBox(@Polymorphic val base: InnerBase?)
+
+@Serializable
+internal data class OuterImpl(@Polymorphic val base: InnerBase, @Polymorphic val base2: InnerBase) : OuterBase
+
+@Serializable
+internal data class OuterNullableImpl(@Polymorphic val base: InnerBase?, @Polymorphic val base2: InnerBase?) : OuterBase
+
+@Serializable
+internal data class OuterBox(@Polymorphic val outerBase: OuterBase, @Polymorphic val innerBase: InnerBase)
+
+@Serializable
+internal data class OuterNullableBox(@Polymorphic val outerBase: OuterBase?, @Polymorphic val innerBase: InnerBase?)
+
+@SharedImmutable
+internal val polymorphicTestModule = SerializersModule {
+ polymorphic(InnerBase::class) {
+ subclass(InnerImpl.serializer())
+ subclass(InnerImpl2.serializer())
+ }
+
+ polymorphic(OuterBase::class) {
+ subclass(OuterImpl.serializer())
+ subclass(OuterNullableImpl.serializer())
+ }
+}
+
+@SharedImmutable
+internal val polymorphicJson = Json {
+ serializersModule = polymorphicTestModule
+ encodeDefaults = true
+}
+
+@SharedImmutable
+internal val polymorphicRelaxedJson = Json {
+ isLenient = true
+ serializersModule = polymorphicTestModule
+ encodeDefaults = true
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonArraySerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonArraySerializerTest.kt
new file mode 100644
index 00000000..ba4672a1
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonArraySerializerTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.serializers
+
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.test.*
+
+import kotlin.test.*
+
+class JsonArraySerializerTest : JsonTestBase() {
+
+ private val expected = "{\"array\":[1,null,[\"nested literal\"],[],{\"key\":\"value\"}]}"
+ private val expectedTopLevel = "[1,null,[\"nested literal\"],[],{\"key\":\"value\"}]"
+
+ @Test
+ fun testJsonArray() = parametrizedTest(default) {
+ assertStringFormAndRestored(expected, JsonArrayWrapper(prebuiltJson()), JsonArrayWrapper.serializer())
+ }
+
+ @Test
+ fun testJsonArrayAsElement() = parametrizedTest(default) {
+ assertStringFormAndRestored(expected.replace("array", "element"), JsonElementWrapper(prebuiltJson()), JsonElementWrapper.serializer())
+ }
+
+ @Test
+ fun testTopLevelJsonObjectAsElement() = parametrizedTest(default) {
+ assertStringFormAndRestored(expectedTopLevel, prebuiltJson(), JsonElementSerializer)
+ }
+
+ @Test
+ fun testJsonArrayToString() {
+ val prebuiltJson = prebuiltJson()
+ val string = lenient.encodeToString(JsonArraySerializer, prebuiltJson)
+ assertEquals(string, prebuiltJson.toString())
+ }
+
+ @Test
+ fun testMixedLiterals() = parametrizedTest { jsonTestingMode ->
+ val json = """[1, "2", 3, "4"]"""
+ val array = default.decodeFromString(JsonArraySerializer, json, jsonTestingMode)
+ array.forEachIndexed { index, element ->
+ require(element is JsonLiteral)
+ assertEquals(index % 2 == 1, element.isString)
+ }
+ }
+
+ @Test
+ fun testMissingCommas() = parametrizedTest { jsonTestingMode ->
+ val message = "Expected end of the array or comma"
+ testFails("[a b c]", message, jsonTestingMode)
+ testFails("[ 1 2 3 ]", message, jsonTestingMode)
+ testFails("[null 1 null]", message, jsonTestingMode)
+ testFails("[1 \n 2]", message, jsonTestingMode)
+ }
+
+ @Test
+ fun testEmptyArray() = parametrizedTest { jsonTestingMode ->
+ assertEquals(JsonArray(emptyList()), lenient.decodeFromString(JsonArraySerializer, "[]", jsonTestingMode))
+ assertEquals(JsonArray(emptyList()), lenient.decodeFromString(JsonArraySerializer, "[ ]", jsonTestingMode))
+ assertEquals(JsonArray(emptyList()), lenient.decodeFromString(JsonArraySerializer, "[\n\n]", jsonTestingMode))
+ assertEquals(JsonArray(emptyList()), lenient.decodeFromString(JsonArraySerializer, "[ \t]", jsonTestingMode))
+ }
+
+ @Test
+ fun testWhitespaces() = parametrizedTest { jsonTestingMode ->
+ assertEquals(
+ JsonArray(listOf(1, 2, 3, 4, 5).map(::JsonPrimitive)),
+ lenient.decodeFromString(JsonArraySerializer, "[1, 2, 3, \n 4, 5]", jsonTestingMode)
+ )
+ }
+
+ @Test
+ fun testExcessiveCommas() = parametrizedTest { jsonTestingMode ->
+ val trailing = "Unexpected trailing comma"
+ val leading = "Unexpected leading comma"
+ testFails("[a,]", trailing, jsonTestingMode)
+ testFails("[,1]", leading, jsonTestingMode)
+ testFails("[,1,]", leading, jsonTestingMode)
+ testFails("[,]", leading, jsonTestingMode)
+ testFails("[,,]", leading, jsonTestingMode)
+ testFails("[,,1]", leading, jsonTestingMode)
+ testFails("[1,,]", trailing, jsonTestingMode)
+ testFails("[1,,2]", trailing, jsonTestingMode)
+ testFails("[, ,]", leading, jsonTestingMode)
+ testFails("[,\n,]", leading, jsonTestingMode)
+ }
+
+ private fun testFails(input: String, errorMessage: String, jsonTestingMode: JsonTestingMode) {
+ assertFailsWithMessage<JsonDecodingException>(errorMessage) {
+ lenient.decodeFromString(
+ JsonArraySerializer,
+ input,
+ jsonTestingMode
+ )
+ }
+ }
+
+ private fun prebuiltJson(): JsonArray {
+ return buildJsonArray {
+ add(1)
+ add(JsonNull)
+ addJsonArray {
+ add("nested literal")
+ }
+ addJsonArray {}
+ addJsonObject {
+ put("key", "value")
+ }
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonNativePrimitivesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonNativePrimitivesTest.kt
new file mode 100644
index 00000000..0afbc052
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonNativePrimitivesTest.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.serializers
+
+import kotlinx.serialization.json.JsonTestBase
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlin.Char.*
+import kotlin.test.Test
+
+class JsonNativePrimitivesTest : JsonTestBase() {
+ @Test
+ fun testTopLevelNativeInt() = assertJsonFormAndRestored(Int.serializer(), 42, "42", default)
+
+ @Test
+ fun testTopLevelNativeString() = assertJsonFormAndRestored(String.serializer(), "42", "\"42\"", default)
+
+ @Test
+ fun testTopLevelNativeChar() = assertJsonFormAndRestored(Char.serializer(), '4', "\"4\"", default)
+
+ @Test
+ fun testTopLevelNativeBoolean() = assertJsonFormAndRestored(Boolean.serializer(), true, "true", default)
+
+ @Test
+ fun testTopLevelNativeNullable() =
+ assertJsonFormAndRestored(Int.serializer().nullable, null, "null", default)
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonNullSerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonNullSerializerTest.kt
new file mode 100644
index 00000000..83de5928
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonNullSerializerTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.serializers
+
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.test.assertStringFormAndRestored
+import kotlin.test.*
+
+class JsonNullSerializerTest : JsonTestBase() {
+
+ @Test
+ fun testJsonNull() = parametrizedTest(default) {
+ assertStringFormAndRestored("{\"element\":null}", JsonNullWrapper(JsonNull), JsonNullWrapper.serializer())
+ }
+
+ @Test
+ fun testJsonNullFailure() = parametrizedTest(default) {
+ val t = assertFailsWith<JsonException> { default.decodeFromString(JsonNullWrapper.serializer(), "{\"element\":\"foo\"}", JsonTestingMode.STREAMING) }
+ assertTrue { t.message!!.contains("'null' literal") }
+ }
+
+ @Test
+ fun testJsonNullAsElement() = parametrizedTest(default) {
+ assertStringFormAndRestored("{\"element\":null}", JsonElementWrapper(JsonNull), JsonElementWrapper.serializer())
+ }
+
+ @Test
+ fun testJsonNullAsPrimitive() = parametrizedTest(default) {
+ assertStringFormAndRestored("{\"primitive\":null}", JsonPrimitiveWrapper(JsonNull), JsonPrimitiveWrapper.serializer())
+ }
+
+ @Test
+ fun testTopLevelJsonNull() = parametrizedTest { jsonTestingMode ->
+ val string = default.encodeToString(JsonNullSerializer, JsonNull, jsonTestingMode)
+ assertEquals("null", string)
+ assertEquals(JsonNull, default.decodeFromString(JsonNullSerializer, string, jsonTestingMode))
+ }
+
+ @Test
+ fun testTopLevelJsonNullAsElement() = parametrizedTest { jsonTestingMode ->
+ val string = default.encodeToString(JsonElementSerializer, JsonNull, jsonTestingMode)
+ assertEquals("null", string)
+ assertEquals(JsonNull, default.decodeFromString(JsonElementSerializer, string, jsonTestingMode))
+ }
+
+ @Test
+ fun testTopLevelJsonNullAsPrimitive() = parametrizedTest { jsonTestingMode ->
+ val string = default.encodeToString(JsonPrimitiveSerializer, JsonNull, jsonTestingMode)
+ assertEquals("null", string)
+ assertEquals(JsonNull, default.decodeFromString(JsonPrimitiveSerializer, string, jsonTestingMode))
+ }
+
+ @Test
+ fun testJsonNullToString() {
+ val string = default.encodeToString(JsonPrimitiveSerializer, JsonNull)
+ assertEquals(string, JsonNull.toString())
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonObjectSerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonObjectSerializerTest.kt
new file mode 100644
index 00000000..7a45c8dd
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonObjectSerializerTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.serializers
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class JsonObjectSerializerTest : JsonTestBase() {
+
+ private val expected = """{"element":{"literal":1,"nullKey":null,"nested":{"another literal":"some value"},"\\. escaped":"\\. escaped","\n new line":"\n new line"}}"""
+ private val expectedTopLevel = """{"literal":1,"nullKey":null,"nested":{"another literal":"some value"},"\\. escaped":"\\. escaped","\n new line":"\n new line"}"""
+
+ @Test
+ fun testJsonObject() = parametrizedTest(default) {
+ assertStringFormAndRestored(expected, JsonObjectWrapper(prebuiltJson()), JsonObjectWrapper.serializer())
+ }
+
+ @Test
+ fun testJsonObjectAsElement() = parametrizedTest(default) {
+ assertStringFormAndRestored(expected, JsonElementWrapper(prebuiltJson()), JsonElementWrapper.serializer())
+ }
+
+ @Test
+ fun testTopLevelJsonObject() = parametrizedTest (default) {
+ assertStringFormAndRestored(expectedTopLevel, prebuiltJson(), JsonObjectSerializer)
+ }
+
+ @Test
+ fun testTopLevelJsonObjectAsElement() = parametrizedTest (default) {
+ assertStringFormAndRestored(expectedTopLevel, prebuiltJson(), JsonElementSerializer)
+ }
+
+ @Test
+ fun testJsonObjectToString() {
+ val prebuiltJson = prebuiltJson()
+ val string = lenient.encodeToString(JsonElementSerializer, prebuiltJson)
+ assertEquals(string, prebuiltJson.toString())
+ }
+
+ @Test
+ fun testDocumentationSample() {
+ val string = Json.encodeToString(JsonElementSerializer, buildJsonObject { put("key", 1.0) })
+ val literal = Json.decodeFromString(JsonElementSerializer, string)
+ assertEquals(JsonObject(mapOf("key" to JsonPrimitive(1.0))), literal)
+ }
+
+ @Test
+ fun testMissingCommas() = parametrizedTest { jsonTestingMode ->
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{ \"1\": \"2\" \"3\":\"4\"}", jsonTestingMode) }
+ }
+
+ @Test
+ fun testEmptyObject() = parametrizedTest { jsonTestingMode ->
+ assertEquals(JsonObject(emptyMap()), lenient.decodeFromString(JsonObjectSerializer, "{}", jsonTestingMode))
+ assertEquals(JsonObject(emptyMap()), lenient.decodeFromString(JsonObjectSerializer, "{}", jsonTestingMode))
+ assertEquals(JsonObject(emptyMap()), lenient.decodeFromString(JsonObjectSerializer, "{\n\n}", jsonTestingMode))
+ assertEquals(JsonObject(emptyMap()), lenient.decodeFromString(JsonObjectSerializer, "{ \t}", jsonTestingMode))
+ }
+
+ @Test
+ fun testInvalidObject() = parametrizedTest { jsonTestingMode ->
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(JsonObjectSerializer, "{\"a\":\"b\"]", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(JsonObjectSerializer, "{", jsonTestingMode) }
+ if (jsonTestingMode != JsonTestingMode.JAVA_STREAMS) // Streams support dangling characters
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(JsonObjectSerializer, "{}}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString(JsonObjectSerializer, "{]", jsonTestingMode) }
+ }
+
+ @Test
+ fun testWhitespaces() = parametrizedTest { jsonTestingMode ->
+ assertEquals(
+ JsonObject(mapOf("1" to JsonPrimitive(2), "3" to JsonPrimitive(4), "5" to JsonPrimitive(6))),
+ lenient.decodeFromString(JsonObjectSerializer, "{1: 2, 3: \n 4, 5:6}", jsonTestingMode)
+ )
+ }
+
+ @Test
+ fun testExcessiveCommas() = parametrizedTest { jsonTestingMode ->
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{\"a\":\"b\",}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{\"a\",}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{,\"1\":\"2\"}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{,\"1\":\"2\",}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{,}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{,,}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{,,\"1\":\"2\"}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{\"1\":\"2\",,}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{\"1\":\"2\",,\"2\":\"2\"}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{, ,}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(JsonObjectSerializer, "{,\n,}", jsonTestingMode) }
+ }
+
+ @Serializable
+ data class Holder(val a: String)
+
+ @Test
+ fun testExcessiveCommasInObject() = parametrizedTest { jsonTestingMode ->
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(Holder.serializer(), "{\"a\":\"b\",}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(Holder.serializer(), "{\"a\",}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(Holder.serializer(), "{,\"a\":\"b\"}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(Holder.serializer(), "{,\"a\":\"b\",}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(Holder.serializer(), "{,}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(Holder.serializer(), "{,,}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(Holder.serializer(), "{,,\"a\":\"b\"}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(Holder.serializer(), "{\"a\":\"b\",,}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(Holder.serializer(), "{, ,}", jsonTestingMode) }
+ assertFailsWith<JsonDecodingException> { lenient.decodeFromString(Holder.serializer(), "{,\n,}", jsonTestingMode) }
+ }
+
+ private fun prebuiltJson(): JsonObject {
+ return buildJsonObject {
+ put("literal", 1)
+ put("nullKey", JsonNull)
+ putJsonObject("nested") {
+ put("another literal", "some value")
+ }
+ put("\\. escaped", "\\. escaped")
+ put("\n new line", "\n new line")
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonPrimitiveSerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonPrimitiveSerializerTest.kt
new file mode 100644
index 00000000..789fb2ca
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonPrimitiveSerializerTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.serializers
+
+import kotlinx.serialization.json.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class JsonPrimitiveSerializerTest : JsonTestBase() {
+
+ @Test
+ fun testJsonPrimitiveDouble() = parametrizedTest { jsonTestingMode ->
+ if (isJs()) return@parametrizedTest // JS toString numbers
+
+
+ val wrapper = JsonPrimitiveWrapper(JsonPrimitive(1.0))
+ val string = default.encodeToString(JsonPrimitiveWrapper.serializer(), wrapper, jsonTestingMode)
+ assertEquals("{\"primitive\":1.0}", string)
+ assertEquals(JsonPrimitiveWrapper(JsonPrimitive(1.0)), default.decodeFromString(JsonPrimitiveWrapper.serializer(), string, jsonTestingMode))
+ }
+
+ @Test
+ fun testJsonPrimitiveInt() = parametrizedTest { jsonTestingMode ->
+ val wrapper = JsonPrimitiveWrapper(JsonPrimitive(1))
+ val string = default.encodeToString(JsonPrimitiveWrapper.serializer(), wrapper, jsonTestingMode)
+ assertEquals("{\"primitive\":1}", string)
+ assertEquals(JsonPrimitiveWrapper(JsonPrimitive(1)), default.decodeFromString(JsonPrimitiveWrapper.serializer(), string, jsonTestingMode))
+ }
+
+
+ @Test
+ fun testJsonPrimitiveString() = parametrizedTest { jsonTestingMode ->
+ val wrapper = JsonPrimitiveWrapper(JsonPrimitive("foo"))
+ val string = default.encodeToString(JsonPrimitiveWrapper.serializer(), wrapper, jsonTestingMode)
+ assertEquals("{\"primitive\":\"foo\"}", string)
+ assertEquals(JsonPrimitiveWrapper(JsonPrimitive("foo")), default.decodeFromString(JsonPrimitiveWrapper.serializer(), string, jsonTestingMode))
+ }
+
+ @Test
+ fun testJsonPrimitiveStringNumber() = parametrizedTest { jsonTestingMode ->
+ val wrapper = JsonPrimitiveWrapper(JsonPrimitive("239"))
+ val string = default.encodeToString(JsonPrimitiveWrapper.serializer(), wrapper, jsonTestingMode)
+ assertEquals("{\"primitive\":\"239\"}", string)
+ assertEquals(JsonPrimitiveWrapper(JsonPrimitive("239")), default.decodeFromString(JsonPrimitiveWrapper.serializer(), string, jsonTestingMode))
+ }
+
+ @Test
+ fun testTopLevelPrimitive() = parametrizedTest { jsonTestingMode ->
+ val string = default.encodeToString(JsonPrimitiveSerializer, JsonPrimitive(42), jsonTestingMode)
+ assertEquals("42", string)
+ assertEquals(JsonPrimitive(42), default.decodeFromString(JsonPrimitiveSerializer, string))
+ }
+
+ @Test
+ fun testTopLevelPrimitiveAsElement() = parametrizedTest { jsonTestingMode ->
+ if (isJs()) return@parametrizedTest // JS toString numbers
+ val string = default.encodeToString(JsonElementSerializer, JsonPrimitive(1.3), jsonTestingMode)
+ assertEquals("1.3", string)
+ assertEquals(JsonPrimitive(1.3), default.decodeFromString(JsonElementSerializer, string, jsonTestingMode))
+ }
+
+ @Test
+ fun testJsonLiteralStringToString() {
+ val literal = JsonPrimitive("some string literal")
+ val string = default.encodeToString(JsonPrimitiveSerializer, literal)
+ assertEquals(string, literal.toString())
+ }
+
+ @Test
+ fun testJsonLiteralIntToString() {
+ val literal = JsonPrimitive(0)
+ val string = default.encodeToString(JsonPrimitiveSerializer, literal)
+ assertEquals(string, literal.toString())
+ }
+
+ @Test
+ fun testJsonLiterals() {
+ testLiteral(0L, "0")
+ testLiteral(0, "0")
+ testLiteral(0.0, "0.0")
+ testLiteral(0.0f, "0.0")
+ testLiteral(Long.MAX_VALUE, "9223372036854775807")
+ testLiteral(Long.MIN_VALUE, "-9223372036854775808")
+ testLiteral(Float.MAX_VALUE, "3.4028235E38")
+ testLiteral(Float.MIN_VALUE, "1.4E-45")
+ testLiteral(Double.MAX_VALUE, "1.7976931348623157E308")
+ testLiteral(Double.MIN_VALUE, "4.9E-324")
+ testLiteral(Int.MAX_VALUE, "2147483647")
+ testLiteral(Int.MIN_VALUE, "-2147483648")
+ }
+
+ private fun testLiteral(number: Number, jvmExpectedString: String) {
+ val literal = JsonPrimitive(number)
+ val string = default.encodeToString(JsonPrimitiveSerializer, literal)
+ assertEquals(string, literal.toString())
+ if (isJvm()) { // We can rely on stable double/float format only on JVM
+ assertEquals(string, jvmExpectedString)
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonSerializerInGenericsTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonSerializerInGenericsTest.kt
new file mode 100644
index 00000000..530fc16f
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonSerializerInGenericsTest.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.serializers
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.test.assertStringFormAndRestored
+import kotlin.test.*
+
+class JsonSerializerInGenericsTest : JsonTestBase() {
+
+ @Serializable
+ data class NonTrivialClass(
+ val list: List<JsonElement?>,
+ val nullableNull: JsonNull?,
+ val nestedMap: Map<String, Map<String, JsonElement?>>
+ )
+
+ private val expected = "{\"list\":[42,[{\"key\":\"value\"}],null],\"nullableNull\":null,\"nestedMap\":{\"key1\":{\"nested\":{\"first\":\"second\"},\"nullable\":null}}}"
+
+ @Test
+ fun testGenericsWithNulls() = parametrizedTest(default) {
+ assertStringFormAndRestored(expected, create(), NonTrivialClass.serializer())
+ }
+
+ private fun create(): NonTrivialClass {
+ return NonTrivialClass(
+ arrayListOf(JsonPrimitive(42), buildJsonArray { addJsonObject { put("key", "value") } }, null),
+ null,
+ mapOf("key1" to mapOf("nested" to buildJsonObject {
+ put("first", "second")
+ }, "nullable" to null))
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonTreeTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonTreeTest.kt
new file mode 100644
index 00000000..00a78a21
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/JsonTreeTest.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.serializers
+
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+class JsonTreeTest : JsonTestBase() {
+
+ private fun parse(input: String): JsonElement = default.decodeFromString(JsonElementSerializer, input)
+
+ @Test
+ fun testParseWithoutExceptions() {
+ val input = """{"a": "foo", "b": 10, "c": ["foo", 100500, {"bar": "baz"}]}"""
+ parse(input)
+ }
+
+ @Test
+ fun testJsonLiteral() {
+ val v = JsonPrimitive("foo")
+ assertEquals(v, parse("\"foo\""))
+ }
+
+ @Test
+ fun testJsonObject() {
+ val input = """{"a": "foo", "b": 10, "c": true, "d": null}"""
+ val elem = parse(input)
+
+ assertTrue(elem is JsonObject)
+ assertEquals(setOf("a", "b", "c", "d"), elem.keys)
+
+ assertEquals(JsonPrimitive("foo"), elem["a"])
+ assertEquals(10, elem["b"]?.jsonPrimitive?.int)
+ assertEquals(true, elem["c"]?.jsonPrimitive?.boolean)
+ assertSame(elem.getValue("d") as JsonNull, JsonNull)
+ }
+
+ @Test
+ fun testJsonObjectWithArrays() {
+ val input = """{"a": "foo", "b": 10, "c": ["foo", 100500, {"bar": "baz"}]}"""
+ val elem = parse(input)
+
+ assertTrue(elem is JsonObject)
+ assertEquals(setOf("a", "b", "c"), elem.keys)
+ assertTrue(elem.getValue("c") is JsonArray)
+
+ val array = elem.getValue("c").jsonArray
+ assertEquals("foo", array.getOrNull(0)?.jsonPrimitive?.content)
+ assertEquals(100500, array.getOrNull(1)?.jsonPrimitive?.int)
+
+ assertTrue(array[2] is JsonObject)
+ val third = array[2].jsonObject
+ assertEquals("baz", third.getValue("bar").jsonPrimitive.content)
+ }
+
+ @Test
+ fun testSaveToJson() {
+ val input = """{"a":"foo","b":10,"c":true,"d":null,"e":["foo",100500,{"bar":"baz"}]}"""
+ val elem = parse(input)
+ val json = elem.toString()
+ assertEquals(input, json)
+ }
+
+ @Test
+ fun testEqualityTest() {
+ val input = """{"a": "foo", "b": 10}"""
+ val parsed = parse(input)
+ val parsed2 = parse(input)
+ val handCrafted = buildJsonObject { put("a", JsonPrimitive("foo")); put("b", JsonPrimitive(10)) }
+ assertEquals(parsed, parsed2)
+ assertEquals(parsed, handCrafted)
+ }
+
+ @Test
+ fun testInEqualityTest() {
+ val input = """{"a": "10", "b": 10}"""
+ val parsed = parse(input) as JsonObject
+ val handCrafted = buildJsonObject { put("a", JsonPrimitive("10")); put("b", JsonPrimitive(10)) }
+ assertEquals(parsed, handCrafted)
+
+ assertNotEquals(parsed["a"], parsed["b"])
+ assertNotEquals(parsed["a"], handCrafted["b"])
+ assertNotEquals(handCrafted["a"], parsed["b"])
+ assertNotEquals(handCrafted["a"], handCrafted["b"])
+ }
+
+ @Test
+ fun testExceptionalState() {
+ val tree =
+ JsonObject(mapOf("a" to JsonPrimitive(42), "b" to JsonArray(listOf(JsonNull)), "c" to JsonPrimitive(false)))
+ assertFailsWith<NoSuchElementException> { tree.getValue("no key").jsonObject }
+ assertFailsWith<IllegalArgumentException> { tree.getValue("a").jsonArray }
+ assertEquals(null, tree["no key"]?.jsonObject)
+ assertEquals(null, tree["a"] as? JsonArray)
+
+ val n = tree.getValue("b").jsonArray[0].jsonPrimitive
+ assertFailsWith<NumberFormatException> { n.int }
+ assertEquals(null, n.intOrNull)
+
+ assertFailsWith<IllegalStateException> { n.boolean }
+ assertEquals(null, n.booleanOrNull)
+ }
+
+ @Test
+ fun testThatJsonArraysCompareWithLists() {
+ val jsonArray: List<JsonElement> = JsonArray(listOf(JsonPrimitive(3), JsonPrimitive(4)))
+ val arrayList: List<JsonElement> = ArrayList(listOf(JsonPrimitive(3), JsonPrimitive(4)))
+ val otherArrayList: List<JsonElement> = ArrayList(listOf(JsonPrimitive(3), JsonPrimitive(5)))
+
+ assertEquals(jsonArray, arrayList)
+ assertEquals(arrayList, jsonArray)
+ assertEquals(jsonArray.hashCode(), arrayList.hashCode())
+ assertNotEquals(jsonArray, otherArrayList)
+ }
+
+ @Test
+ fun testThatJsonObjectsCompareWithMaps() {
+ val jsonObject: Map<String, JsonElement> = JsonObject(
+ mapOf(
+ "three" to JsonPrimitive(3),
+ "four" to JsonPrimitive(4)
+ )
+ )
+ val hashMap: Map<String, JsonElement> = HashMap(
+ mapOf(
+ "three" to JsonPrimitive(3),
+ "four" to JsonPrimitive(4)
+ )
+ )
+ val otherJsonObject: Map<String, JsonElement> = JsonObject(
+ mapOf(
+ "three" to JsonPrimitive(3),
+ "five" to JsonPrimitive(5)
+ )
+ )
+ val otherHashMap: Map<String, JsonElement> = HashMap(
+ mapOf(
+ "three" to JsonPrimitive(3),
+ "four" to JsonPrimitive(5)
+ )
+ )
+
+ assertEquals(jsonObject, hashMap)
+ assertEquals(hashMap, jsonObject)
+ assertEquals(jsonObject.hashCode(), hashMap.hashCode())
+ assertNotEquals(jsonObject, otherHashMap)
+ assertNotEquals(jsonObject, otherJsonObject)
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/serializers/Primitives.kt b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/Primitives.kt
new file mode 100644
index 00000000..34c6dc88
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/serializers/Primitives.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.serializers
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class JsonElementWrapper(val element: JsonElement)
+
+@Serializable
+data class JsonPrimitiveWrapper(val primitive: JsonPrimitive)
+
+@Serializable
+data class JsonNullWrapper(val element: JsonNull)
+
+@Serializable
+data class JsonObjectWrapper(val element: JsonObject)
+
+@Serializable
+data class JsonArrayWrapper(val array: JsonArray) \ No newline at end of file
diff --git a/formats/json/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionInSealedClassesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionInSealedClassesTest.kt
new file mode 100644
index 00000000..18a49a52
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionInSealedClassesTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.modules
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+private const val prefix = "kotlinx.serialization.modules.SerialNameCollisionInSealedClassesTest"
+
+class SerialNameCollisionInSealedClassesTest {
+ @Serializable
+ sealed class Base {
+ @Serializable
+ data class Child(val type: String, @SerialName("type2") val f: String = "2") : Base()
+ }
+
+ private fun Json(discriminator: String, useArrayPolymorphism: Boolean = false) = Json {
+ classDiscriminator = discriminator
+ this.useArrayPolymorphism = useArrayPolymorphism
+ }
+
+ @Test
+ fun testCollisionWithDiscriminator() {
+ assertFailsWith<IllegalStateException> { Json("type").encodeToString(Base.serializer(), Base.Child("a")) }
+ assertFailsWith<IllegalStateException> { Json("type2").encodeToString(Base.serializer(), Base.Child("a")) }
+ Json("f").encodeToString(Base.serializer(), Base.Child("a"))
+ }
+
+ @Test
+ fun testNoCollisionWithArrayPolymorphism() {
+ Json("type", true).encodeToString(Base.serializer(), Base.Child("a"))
+ }
+
+ @Serializable
+ sealed class BaseCollision {
+ @Serializable
+ class Child() : BaseCollision()
+
+ @Serializable
+ @SerialName("$prefix.BaseCollision.Child")
+ class ChildCollided() : BaseCollision()
+ }
+
+ @Test
+ fun testDescriptorInitializerFailure() {
+ BaseCollision.Child()
+ BaseCollision.ChildCollided()
+ BaseCollision.ChildCollided.serializer().descriptor // Doesn't fail
+ assertFailsWith<IllegalStateException> { BaseCollision.serializer().descriptor }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionTest.kt b/formats/json/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionTest.kt
new file mode 100644
index 00000000..4a88cb12
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.modules
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+private const val prefix = "kotlinx.serialization.modules.SerialNameCollisionTest"
+
+class SerialNameCollisionTest {
+
+ // Polymorphism
+ interface IBase
+
+ @Serializable
+ abstract class Base : IBase
+
+ @Serializable
+ data class Derived(val type: String, val type2: String) : Base()
+
+ @Serializable
+ data class DerivedCustomized(
+ @SerialName("type") val t: String, @SerialName("type2") val t2: String, val t3: String
+ ) : Base()
+
+ @Serializable
+ @SerialName("$prefix.Derived")
+ data class DerivedRenamed(val type: String, val type2: String) : Base()
+
+ private fun Json(discriminator: String, context: SerializersModule, useArrayPolymorphism: Boolean = false) = Json {
+ classDiscriminator = discriminator
+ this.useArrayPolymorphism = useArrayPolymorphism
+ serializersModule = context
+
+ }
+
+ @Test
+ fun testCollisionWithDiscriminator() {
+ val module = SerializersModule {
+ polymorphic(Base::class) {
+ subclass(Derived.serializer())
+ }
+ }
+
+ assertFailsWith<IllegalArgumentException> { Json("type", module) }
+ assertFailsWith<IllegalArgumentException> { Json("type2", module) }
+ Json("type3", module) // OK
+ }
+
+ @Test
+ fun testNoCollisionWithArrayPolymorphism() {
+ val module = SerializersModule {
+ polymorphic(Base::class) {
+ subclass(Derived.serializer())
+ }
+ }
+ Json("type", module, true)
+ }
+
+ @Test
+ fun testCollisionWithDiscriminatorViaSerialNames() {
+ val module = SerializersModule {
+ polymorphic(Base::class) {
+ subclass(DerivedCustomized.serializer())
+ }
+ }
+
+ assertFailsWith<IllegalArgumentException> { Json("type", module) }
+ assertFailsWith<IllegalArgumentException> { Json("type2", module) }
+ assertFailsWith<IllegalArgumentException> { Json("t3", module) }
+ Json("t4", module) // OK
+
+ }
+
+ @Test
+ fun testCollisionWithinHierarchy() {
+ SerializersModule {
+ assertFailsWith<IllegalArgumentException> {
+ polymorphic(Base::class) {
+ subclass(Derived.serializer())
+ subclass(DerivedRenamed.serializer())
+ }
+ }
+ }
+ }
+
+ @Test
+ fun testCollisionWithinHierarchyViaConcatenation() {
+ val module = SerializersModule {
+ polymorphic(Base::class) {
+ subclass(Derived.serializer())
+ }
+ }
+ val module2 = SerializersModule {
+ polymorphic(Base::class) {
+ subclass(DerivedRenamed.serializer())
+ }
+ }
+
+ assertFailsWith<IllegalArgumentException> { module + module2 }
+ }
+
+ @Test
+ fun testNoCollisionWithinHierarchy() {
+ val module = SerializersModule {
+ polymorphic(Base::class) {
+ subclass(Derived.serializer())
+ }
+
+ polymorphic(IBase::class) {
+ subclass(DerivedRenamed.serializer())
+ }
+ }
+
+ assertSame(Derived.serializer(), module.getPolymorphic(Base::class, "$prefix.Derived"))
+ assertSame(
+ DerivedRenamed.serializer(),
+ module.getPolymorphic(IBase::class, "$prefix.Derived")
+ )
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/test/ContextualTest.kt b/formats/json/commonTest/src/kotlinx/serialization/test/ContextualTest.kt
new file mode 100644
index 00000000..0b34f1c7
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/test/ContextualTest.kt
@@ -0,0 +1,47 @@
+@file:UseContextualSerialization(ContextualTest.Cont::class)
+
+package kotlinx.serialization.test
+
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.UseContextualSerialization
+import kotlinx.serialization.descriptors.PrimitiveKind
+import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.modules.SerializersModule
+import kotlinx.serialization.modules.contextual
+
+class ContextualTest {
+ data class Cont(val i: Int)
+
+ @Serializable
+ data class DateHolder(val cont: Cont?)
+
+ object DateSerializer: KSerializer<Cont> {
+ override fun deserialize(decoder: Decoder): Cont {
+ return Cont(decoder.decodeInt())
+ }
+
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ContSerializer", PrimitiveKind.INT)
+
+ override fun serialize(encoder: Encoder, value: Cont) {
+ encoder.encodeInt(value.i)
+ }
+
+ }
+
+ val module = SerializersModule {
+ contextual(DateSerializer)
+ }
+
+ @kotlin.test.Test
+ fun test() {
+ val json = Json { serializersModule = module }
+
+ println(json.encodeToString(DateHolder(Cont(42))))
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt b/formats/json/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt
new file mode 100644
index 00000000..c4a6b986
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+enum class Platform {
+ JVM, JS_LEGACY, JS_IR, NATIVE
+}
+
+public expect val currentPlatform: Platform
+
+public fun isJs(): Boolean = currentPlatform == Platform.JS_LEGACY || currentPlatform == Platform.JS_IR
+public fun isJsLegacy(): Boolean = currentPlatform == Platform.JS_LEGACY
+public fun isJvm(): Boolean = currentPlatform == Platform.JVM
+public fun isNative(): Boolean = currentPlatform == Platform.NATIVE
diff --git a/formats/json/commonTest/src/kotlinx/serialization/test/InternalHexConverter.kt b/formats/json/commonTest/src/kotlinx/serialization/test/InternalHexConverter.kt
new file mode 100644
index 00000000..349eb43c
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/test/InternalHexConverter.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+object InternalHexConverter {
+ private const val hexCode = "0123456789ABCDEF"
+
+ fun parseHexBinary(s: String): ByteArray {
+ val len = s.length
+ require(len % 2 == 0) { "HexBinary string must be even length" }
+ val bytes = ByteArray(len / 2)
+ var i = 0
+
+ while (i < len) {
+ val h = hexToInt(s[i])
+ val l = hexToInt(s[i + 1])
+ require(!(h == -1 || l == -1)) { "Invalid hex chars: ${s[i]}${s[i+1]}" }
+
+ bytes[i / 2] = ((h shl 4) + l).toByte()
+ i += 2
+ }
+
+ return bytes
+ }
+
+ private fun hexToInt(ch: Char): Int = when (ch) {
+ in '0'..'9' -> ch - '0'
+ in 'A'..'F' -> ch - 'A' + 10
+ in 'a'..'f' -> ch - 'a' + 10
+ else -> -1
+ }
+
+ fun printHexBinary(data: ByteArray, lowerCase: Boolean = false): String {
+ val r = StringBuilder(data.size * 2)
+ for (b in data) {
+ r.append(hexCode[b.toInt() shr 4 and 0xF])
+ r.append(hexCode[b.toInt() and 0xF])
+ }
+ return if (lowerCase) r.toString().lowercase() else r.toString()
+ }
+
+ fun toHexString(n: Int): String {
+ val arr = ByteArray(4)
+ for (i in 0 until 4) {
+ arr[i] = (n shr (24 - i * 8)).toByte()
+ }
+ return printHexBinary(arr, true).trimStart('0').takeIf { it.isNotEmpty() } ?: "0"
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/test/JsonHelpers.kt b/formats/json/commonTest/src/kotlinx/serialization/test/JsonHelpers.kt
new file mode 100644
index 00000000..f3b742e3
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/test/JsonHelpers.kt
@@ -0,0 +1,9 @@
+package kotlinx.serialization.test
+
+import kotlinx.serialization.DeserializationStrategy
+import kotlinx.serialization.SerializationStrategy
+import kotlinx.serialization.json.Json
+
+public expect fun <T> Json.encodeViaStream(serializer: SerializationStrategy<T>, value: T): String
+
+public expect fun <T> Json.decodeViaStream(serializer: DeserializationStrategy<T>, input: String): T
diff --git a/formats/json/commonTest/src/kotlinx/serialization/test/TestHelpers.kt b/formats/json/commonTest/src/kotlinx/serialization/test/TestHelpers.kt
new file mode 100644
index 00000000..d178c871
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/test/TestHelpers.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.serialization.test
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.json.internal.ESCAPE_STRINGS
+import kotlin.random.Random
+import kotlin.random.nextInt
+import kotlin.test.*
+
+fun SerialDescriptor.assertDescriptorEqualsTo(other: SerialDescriptor) {
+ assertEquals(serialName, other.serialName)
+ assertEquals(elementsCount, other.elementsCount)
+ assertEquals(isNullable, other.isNullable)
+ assertEquals(annotations, other.annotations)
+ assertEquals(kind, other.kind)
+ for (i in 0 until elementsCount) {
+ getElementDescriptor(i).assertDescriptorEqualsTo(other.getElementDescriptor(i))
+ val name = getElementName(i)
+ val otherName = other.getElementName(i)
+ assertEquals(name, otherName)
+ assertEquals(getElementAnnotations(i), other.getElementAnnotations(i))
+ assertEquals(name, otherName)
+ assertEquals(isElementOptional(i), other.isElementOptional(i))
+ }
+}
+
+inline fun noJs(test: () -> Unit) {
+ if (!isJs()) test()
+}
+
+inline fun noLegacyJs(test: () -> Unit) {
+ if (!isJsLegacy()) test()
+}
+
+inline fun jvmOnly(test: () -> Unit) {
+ if (isJvm()) test()
+}
+
+inline fun assertFailsWithMissingField(block: () -> Unit) {
+ val e = assertFailsWith<SerializationException>(block = block)
+ assertTrue(e.message?.contains("but it was missing") ?: false)
+}
+
+fun generateRandomUnicodeString(size: Int): String {
+ return buildString(size) {
+ repeat(size) {
+ val pickEscape = Random.nextBoolean()
+ if (pickEscape) {
+ // Definitely an escape symbol
+ append(ESCAPE_STRINGS.random().takeIf { it != null } ?: 'N')
+ } else {
+ // Any symbol, including escaping one
+ append(Char(Random.nextInt(Char.MIN_VALUE.code..Char.MAX_VALUE.code)).takeIf { it.isDefined() && !it.isSurrogate()} ?: 'U')
+ }
+ }
+ }
+}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/test/TestId.kt b/formats/json/commonTest/src/kotlinx/serialization/test/TestId.kt
new file mode 100644
index 00000000..c4af25e5
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/test/TestId.kt
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+
+@SerialInfo
+@Target(AnnotationTarget.PROPERTY)
+annotation class Id(val id: Int)
diff --git a/formats/json/commonTest/src/kotlinx/serialization/test/TestingFramework.kt b/formats/json/commonTest/src/kotlinx/serialization/test/TestingFramework.kt
new file mode 100644
index 00000000..064828a8
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/test/TestingFramework.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+
+inline fun <reified T : Any> assertStringFormAndRestored(
+ expected: String,
+ original: T,
+ serializer: KSerializer<T>,
+ format: StringFormat = Json,
+ printResult: Boolean = false
+) {
+ val string = format.encodeToString(serializer, original)
+ if (printResult) println("[Serialized form] $string")
+ assertEquals(expected, string)
+ val restored = format.decodeFromString(serializer, string)
+ if (printResult) println("[Restored form] $restored")
+ assertEquals(original, restored)
+}
+
+inline fun <reified T : Any> StringFormat.assertStringFormAndRestored(
+ expected: String,
+ original: T,
+ serializer: KSerializer<T>,
+ printResult: Boolean = false
+) {
+ val string = this.encodeToString(serializer, original)
+ if (printResult) println("[Serialized form] $string")
+ assertEquals(expected, string)
+ val restored = this.decodeFromString(serializer, string)
+ if (printResult) println("[Restored form] $restored")
+ assertEquals(original, restored)
+}
+
+fun <T : Any> assertSerializedAndRestored(
+ original: T,
+ serializer: KSerializer<T>,
+ format: StringFormat = Json,
+ printResult: Boolean = false
+) {
+ if (printResult) println("[Input] $original")
+ val string = format.encodeToString(serializer, original)
+ if (printResult) println("[Serialized form] $string")
+ val restored = format.decodeFromString(serializer, string)
+ if (printResult) println("[Restored form] $restored")
+ assertEquals(original, restored)
+}
+
+inline fun <reified T : Throwable> assertFailsWithMessage(
+ message: String,
+ assertionMessage: String? = null,
+ block: () -> Unit
+) {
+ val exception = assertFailsWith(T::class, assertionMessage, block)
+ assertTrue(
+ exception.message!!.contains(message),
+ "Expected message '${exception.message}' to contain substring '$message'"
+ )
+}
diff --git a/formats/json/jsMain/src/kotlinx/serialization/json/Dynamics.kt b/formats/json/jsMain/src/kotlinx/serialization/json/Dynamics.kt
new file mode 100644
index 00000000..836f1604
--- /dev/null
+++ b/formats/json/jsMain/src/kotlinx/serialization/json/Dynamics.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.internal.*
+
+/**
+ * Converts native JavaScript objects into Kotlin ones, verifying their types.
+ *
+ * A result of `decodeFromDynamic(nativeObj)` should be the same as
+ * `kotlinx.serialization.json.Json.decodeFromString(kotlin.js.JSON.stringify(nativeObj))`.
+ * This class also supports array-based polymorphism if the corresponding flag in [Json.configuration] is set to `true`.
+ * Does not support any other [Map] keys than [String].
+ * Has limitation on [Long] type: any JS number that is greater than
+ * [`abs(2^53-1)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)
+ * is considered to be imprecise and therefore can't be deserialized to [Long]. Either use [Double] type
+ * for such values or pass them as strings using [LongAsStringSerializer] afterwards.
+ *
+ * Usage example:
+ *
+ * ```
+ * @Serializable
+ * data class Data(val a: Int)
+ *
+ * @Serializable
+ * data class DataWrapper(val s: String, val d: Data?)
+ *
+ * val dyn: dynamic = js("""{s:"foo", d:{a:42}}""")
+ * val parsed = Json.decodeFromDynamic(DataWrapper.serializer(), dyn)
+ * parsed == DataWrapper("foo", Data(42)) // true
+ * ```
+ */
+@ExperimentalSerializationApi
+public fun <T> Json.decodeFromDynamic(deserializer: DeserializationStrategy<T>, dynamic: dynamic): T = decodeDynamic(deserializer, dynamic)
+
+/**
+ * A reified version of [decodeFromDynamic].
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Json.decodeFromDynamic(dynamic: dynamic): T =
+ decodeFromDynamic(serializersModule.serializer(), dynamic)
+
+/**
+ * Converts Kotlin data structures to plain Javascript objects
+ *
+ * Limitations:
+ * * Map keys must be of primitive or enum type
+ * * All [Long] values must be less than [`abs(2^53-1)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER).
+ * Otherwise, they're encoded as doubles with precision loss and require `isLenient` flag of [Json.configuration] set to true.
+ *
+ * Example of usage:
+ * ```
+ * @Serializable
+ * open class DataWrapper(open val s: String, val d: String?)
+ *
+ * val wrapper = DataWrapper("foo", "bar")
+ * val plainJS: dynamic = Json.encodeToDynamic(DataWrapper.serializer(), wrapper)
+ * ```
+ */
+@ExperimentalSerializationApi
+public fun <T> Json.encodeToDynamic(serializer: SerializationStrategy<T>, value: T): dynamic = encodeDynamic(serializer, value)
+
+/**
+ * A reified version of [encodeToDynamic].
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Json.encodeToDynamic(value: T): dynamic =
+ encodeToDynamic(serializersModule.serializer(), value)
diff --git a/formats/json/jsMain/src/kotlinx/serialization/json/JsonSchemaCache.kt b/formats/json/jsMain/src/kotlinx/serialization/json/JsonSchemaCache.kt
new file mode 100644
index 00000000..a0820ef4
--- /dev/null
+++ b/formats/json/jsMain/src/kotlinx/serialization/json/JsonSchemaCache.kt
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.json.internal.*
+
+@Suppress("DEPRECATION_ERROR")
+internal actual val Json.schemaCache: DescriptorSchemaCache get() = this._schemaCache
diff --git a/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicDecoders.kt b/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicDecoders.kt
new file mode 100644
index 00000000..a6658c7c
--- /dev/null
+++ b/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicDecoders.kt
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.math.*
+
+/**
+ * [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER]
+ */
+internal const val MAX_SAFE_INTEGER: Double = 9007199254740991.toDouble() // 2^53 - 1
+
+@JsName("decodeDynamic")
+internal fun <T> Json.decodeDynamic(deserializer: DeserializationStrategy<T>, dynamic: dynamic): T {
+ val input = when (jsTypeOf(dynamic)) {
+ "boolean", "number", "string" -> PrimitiveDynamicInput(dynamic, this)
+ else -> {
+ if (js("Array.isArray(dynamic)")) {
+ DynamicListInput(dynamic, this)
+ } else {
+ DynamicInput(dynamic, this)
+ }
+ }
+ }
+ return input.decodeSerializableValue(deserializer)
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private open class DynamicInput(
+ protected val value: dynamic,
+ override val json: Json
+) : NamedValueDecoder(), JsonDecoder {
+
+ protected val keys: dynamic = js("Object").keys(value ?: js("{}"))
+ protected open val size: Int = keys.length as Int
+
+ private var forceNull: Boolean = false
+
+ override val serializersModule: SerializersModule
+ get() = json.serializersModule
+
+ private var currentPosition = 0
+
+ override fun decodeJsonElement(): JsonElement {
+ val tag = currentTagOrNull
+ if (tag != null) { // reading a nested value, not the current one
+ return json.decodeFromDynamic(JsonElement.serializer(), value[tag])
+ }
+
+ if (value == null) {
+ return JsonNull
+ }
+
+ return buildJsonObject {
+ for (i in 0 until size) {
+ val key = keys[i]
+ val value = json.decodeDynamic(JsonElement.serializer(), value[key])
+ put(key.toString(), value)
+ }
+ }
+ }
+
+ override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
+ return decodeSerializableValuePolymorphic(deserializer)
+ }
+
+ private fun coerceInputValue(descriptor: SerialDescriptor, index: Int, tag: String): Boolean =
+ json.tryCoerceValue(
+ descriptor.getElementDescriptor(index),
+ { getByTag(tag) == null },
+ { getByTag(tag) as? String }
+ )
+
+ override fun composeName(parentName: String, childName: String): String = childName
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ while (currentPosition < descriptor.elementsCount) {
+ val name = descriptor.getTag(currentPosition++)
+ val index = currentPosition - 1
+ forceNull = false
+ if ((hasName(name) || absenceIsNull(descriptor, index)) && (!json.configuration.coerceInputValues || !coerceInputValue(descriptor, index, name))) {
+ return index
+ }
+ }
+ return CompositeDecoder.DECODE_DONE
+ }
+
+ private fun hasName(name: String) = value[name] !== undefined
+
+ private fun absenceIsNull(descriptor: SerialDescriptor, index: Int): Boolean {
+ forceNull = !json.configuration.explicitNulls
+ && !descriptor.isElementOptional(index) && descriptor.getElementDescriptor(index).isNullable
+ return forceNull
+ }
+
+ override fun elementName(desc: SerialDescriptor, index: Int): String {
+ val mainName = desc.getElementName(index)
+ if (!json.configuration.useAlternativeNames) return mainName
+ // Fast path, do not go through Map.get
+ // Note, it blocks ability to detect collisions between the primary name and alternate,
+ // but it eliminates a significant performance penalty (about -15% without this optimization)
+ if (hasName(mainName)) return mainName
+ // Slow path
+ val alternativeNamesMap =
+ json.schemaCache.getOrPut(desc, JsonAlternativeNamesKey, desc::buildAlternativeNamesMap)
+ val nameInObject = (keys as Array<String>).find { alternativeNamesMap[it] == index }
+ return nameInObject ?: mainName
+ }
+
+ override fun decodeTaggedEnum(tag: String, enumDescriptor: SerialDescriptor): Int {
+ val byTag = getByTag(tag)
+ val enumValue = byTag as? String ?: throw SerializationException("Enum value must be a string, got '$byTag'")
+ return enumDescriptor.getJsonNameIndexOrThrow(json, enumValue)
+ }
+
+ protected open fun getByTag(tag: String): dynamic = value[tag]
+
+ override fun decodeTaggedChar(tag: String): Char {
+ return when (val value = getByTag(tag)) {
+ is String -> if (value.length == 1) value[0] else throw SerializationException("$value can't be represented as Char")
+ is Number -> value.toChar()
+ else -> throw SerializationException("$value can't be represented as Char")
+ }
+ }
+
+ override fun decodeTaggedLong(tag: String): Long {
+ val value = getByTag(tag)
+ val number = value as? Double ?: throw SerializationException("$value is not a Number")
+ return toJavascriptLong(number)
+ }
+
+ protected fun toJavascriptLong(number: Double): Long {
+ val canBeConverted = number.isFinite() && floor(number) == number
+ if (!canBeConverted)
+ throw SerializationException("$number can't be represented as Long because it is not finite or has non-zero fractional part")
+ val inBound = abs(number) <= MAX_SAFE_INTEGER
+ if (!inBound)
+ throw SerializationException("$number can't be deserialized to Long due to a potential precision loss")
+ return number.toLong()
+ }
+
+ override fun decodeTaggedValue(tag: String): Any {
+ val o = getByTag(tag) ?: throwMissingTag(tag)
+ return o as Any
+ }
+
+ override fun decodeTaggedNotNullMark(tag: String): Boolean {
+ if (forceNull) {
+ return false
+ }
+
+ val o = getByTag(tag)
+ if (o === undefined) throwMissingTag(tag)
+ @Suppress("SENSELESS_COMPARISON") // null !== undefined !
+ return o != null
+ }
+
+ private fun throwMissingTag(tag: String) {
+ throw SerializationException("Value for field $tag is missing")
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ val currentValue = currentTagOrNull?.let { value[it] } ?: value
+ val kind = when (descriptor.kind) {
+ is PolymorphicKind -> {
+ if (json.configuration.useArrayPolymorphism) StructureKind.LIST
+ else StructureKind.MAP
+ }
+ else -> descriptor.kind
+ }
+ return when (kind) {
+ StructureKind.LIST -> DynamicListInput(currentValue, json)
+ StructureKind.MAP -> DynamicMapInput(currentValue, json)
+ else -> DynamicInput(currentValue, json)
+ }
+ }
+}
+
+private class DynamicMapInput(
+ value: dynamic,
+ json: Json,
+) : DynamicInput(value, json) {
+ override val size: Int = (keys.length as Int) * 2
+ private var currentPosition = -1
+ private val isKey: Boolean get() = currentPosition % 2 == 0
+
+ override fun elementName(desc: SerialDescriptor, index: Int): String {
+ val i = index / 2
+ return keys[i] as String
+ }
+
+ /*
+ * Decode tagger primitives rationale:
+ * In JS, key type of js("{1:2}") is String for any primitive.
+ * In order to properly deserialize them, we should additionally check
+ * for String type, to properly handle it
+ */
+ private fun throwIllegalKeyType(tag: String, value: Any, type: String): Nothing {
+ throw SerializationException("Property $tag is not valid type $type: $value")
+ }
+
+ override fun decodeTaggedByte(tag: String): Byte =
+ decodeMapKey(tag, "byte", { super.decodeTaggedByte(tag) }, { toByteOrNull() })
+
+ override fun decodeTaggedShort(tag: String): Short =
+ decodeMapKey(tag, "short", { super.decodeTaggedShort(tag) }, { toShortOrNull() })
+
+ override fun decodeTaggedInt(tag: String): Int =
+ decodeMapKey(tag, "int", { super.decodeTaggedInt(tag) }, { toIntOrNull() })
+
+ override fun decodeTaggedLong(tag: String): Long = decodeMapKey(tag, "long", { super.decodeTaggedLong(tag) }) {
+ toJavascriptLong(toDoubleOrNull() ?: throwIllegalKeyType(tag, this, "long"))
+ }
+
+ override fun decodeTaggedFloat(tag: String): Float =
+ decodeMapKey(tag, "float", { super.decodeTaggedFloat(tag) }, { toFloatOrNull() })
+
+ override fun decodeTaggedDouble(tag: String): Double =
+ decodeMapKey(tag, "double", { super.decodeTaggedDouble(tag) }, { toDoubleOrNull() })
+
+ private inline fun <reified T> decodeMapKey(
+ tag: String,
+ type: String,
+ decode: (tag: String) -> T,
+ cast: String.() -> T?
+ ): T {
+ if (isKey) {
+ val value = decodeTaggedValue(tag)
+ if (value !is String) return decode(tag)
+ return value.toString().cast() ?: throwIllegalKeyType(tag, value, type)
+ }
+ return decode(tag)
+ }
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ while (currentPosition < size - 1) {
+ val i = currentPosition++ / 2
+ val name = keys[i] as String
+ if (this.value[name] !== undefined) return currentPosition
+ }
+ return CompositeDecoder.DECODE_DONE
+ }
+
+ override fun getByTag(tag: String): dynamic {
+ return if (currentPosition % 2 == 0) tag else value[tag]
+ }
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private class DynamicListInput(
+ value: dynamic,
+ json: Json,
+) : DynamicInput(value, json) {
+ override val size = value.length as Int
+ private var currentPosition = -1
+
+ override fun elementName(desc: SerialDescriptor, index: Int): String = (index).toString()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ while (currentPosition < size - 1) {
+ val o = value[++currentPosition]
+ if (o !== undefined) return currentPosition
+ }
+ return CompositeDecoder.DECODE_DONE
+ }
+
+ override fun decodeJsonElement(): JsonElement {
+ val tag = currentTagOrNull
+ if (tag != null) { // reading a nested value, not the current one
+ return json.decodeFromDynamic(JsonElement.serializer(), value[tag])
+ }
+ return buildJsonArray {
+ for (i in 0 until size) {
+ add(json.decodeFromDynamic(JsonElement.serializer(), value[i]))
+ }
+ }
+ }
+}
+
+private class PrimitiveDynamicInput(
+ value: dynamic,
+ json: Json,
+) : DynamicInput(value, json) {
+ init {
+ pushTag("primitive")
+ }
+
+ override fun getByTag(tag: String): dynamic = value
+
+ override fun decodeJsonElement(): JsonElement {
+ val str = value.toString()
+ return when (jsTypeOf(value)) {
+ "boolean" -> JsonPrimitive(str.toBoolean())
+ "number" -> {
+ val l = str.toLongOrNull()
+ if (l != null) return JsonPrimitive(l)
+ val d = str.toDoubleOrNull()
+ if (d != null) return JsonPrimitive(d)
+ return JsonPrimitive(str)
+ }
+ else -> JsonPrimitive(str)
+ }
+ }
+}
diff --git a/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicEncoders.kt b/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicEncoders.kt
new file mode 100644
index 00000000..4bd46d59
--- /dev/null
+++ b/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicEncoders.kt
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.math.*
+
+/**
+ * Converts Kotlin data structures to plain Javascript objects
+ *
+ *
+ * Limitations:
+ * * Map keys must be of primitive or enum type
+ * * Enums are serialized as the value of `@SerialName` if present or their name, in that order.
+ * * Currently does not support polymorphism
+ *
+ * Example of usage:
+ * ```
+ * @Serializable
+ * open class DataWrapper(open val s: String, val d: String?)
+ *
+ * val wrapper = DataWrapper("foo", "bar")
+ * val plainJS: dynamic = DynamicObjectSerializer().serialize(DataWrapper.serializer(), wrapper)
+ * ```
+ */
+@JsName("encodeToDynamic")
+@OptIn(ExperimentalSerializationApi::class)
+internal fun <T> Json.encodeDynamic(serializer: SerializationStrategy<T>, value: T): dynamic {
+ if (serializer.descriptor.kind is PrimitiveKind || serializer.descriptor.kind is SerialKind.ENUM) {
+ val encoder = DynamicPrimitiveEncoder(this)
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.result
+ }
+ val encoder = DynamicObjectEncoder(this, false)
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.result
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private class DynamicObjectEncoder(
+ override val json: Json,
+ private val encodeNullAsUndefined: Boolean
+) : AbstractEncoder(), JsonEncoder {
+
+ override val serializersModule: SerializersModule
+ get() = json.serializersModule
+
+ var result: dynamic = NoOutputMark
+ private lateinit var current: Node
+ private var currentName: String? = null
+ private lateinit var currentDescriptor: SerialDescriptor
+ private var currentElementIsMapKey = false
+
+ /**
+ * Flag of usage polymorphism with discriminator attribute
+ */
+ private var polymorphicDiscriminator: String? = null
+
+ private object NoOutputMark
+
+ class Node(val writeMode: WriteMode, val jsObject: dynamic) {
+ var index: Int = 0
+ lateinit var parent: Node
+ }
+
+ enum class WriteMode {
+ OBJ, MAP, LIST
+ }
+
+ override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
+ current.index = index
+ currentDescriptor = descriptor
+
+ when {
+ current.writeMode == WriteMode.MAP -> currentElementIsMapKey = current.index % 2 == 0
+ current.writeMode == WriteMode.LIST && descriptor.kind is PolymorphicKind -> currentName = index.toString()
+ else -> currentName = descriptor.getElementName(index)
+ }
+
+ return true
+ }
+
+ override fun encodeValue(value: Any) {
+ if (currentElementIsMapKey) {
+ currentName = value.toString()
+ } else if (isNotStructured()) {
+ result = value
+ } else {
+ current.jsObject[currentName] = value
+ }
+ }
+
+ override fun encodeChar(value: Char) {
+ encodeValue(value.toString())
+ }
+
+ override fun encodeNull() {
+ if (currentElementIsMapKey) {
+ currentName = null
+ } else {
+ if (encodeNullAsUndefined) return // omit element
+
+ current.jsObject[currentName] = null
+ }
+ }
+
+ override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) {
+ encodeValue(enumDescriptor.getElementName(index))
+ }
+
+ override fun encodeLong(value: Long) {
+ val asDouble = value.toDouble()
+ val conversionHasLossOfPrecision = abs(asDouble) > MAX_SAFE_INTEGER
+ // todo: shall it be driven by isLenient or another configuration key?
+ if (!json.configuration.isLenient && conversionHasLossOfPrecision) {
+ throw IllegalArgumentException(
+ "$value can't be serialized to number due to a potential precision loss. " +
+ "Use the JsonConfiguration option isLenient to serialize anyway"
+ )
+ }
+
+ if (currentElementIsMapKey && conversionHasLossOfPrecision) {
+ throw IllegalArgumentException(
+ "Long with value $value can't be used in json as map key, because its value is larger than Number.MAX_SAFE_INTEGER"
+ )
+ }
+
+ encodeValue(asDouble)
+ }
+
+ override fun encodeFloat(value: Float) {
+ encodeDouble(value.toDouble())
+ }
+
+ override fun encodeDouble(value: Double) {
+ if (currentElementIsMapKey) {
+ val hasNonZeroFractionalPart = floor(value) != value
+ if (!value.isFinite() || hasNonZeroFractionalPart) {
+ throw IllegalArgumentException(
+ "Double with value $value can't be used in json as map key, because its value is not an integer."
+ )
+ }
+ }
+ encodeValue(value)
+ }
+
+ override fun <T : Any> encodeNullableSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ serializer: SerializationStrategy<T>,
+ value: T?
+ ) {
+ if (value != null || json.configuration.explicitNulls) {
+ super.encodeNullableSerializableElement(descriptor, index, serializer, value)
+ }
+ }
+
+ override fun encodeJsonElement(element: JsonElement) {
+ encodeSerializableValue(JsonElementSerializer, element)
+ }
+
+ override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int) =
+ json.configuration.encodeDefaults
+
+ private fun enterNode(jsObject: dynamic, writeMode: WriteMode) {
+ val child = Node(writeMode, jsObject)
+ child.parent = current
+ current = child
+ }
+
+ private fun exitNode() {
+ current = current.parent
+ currentElementIsMapKey = false
+ }
+
+ private fun isNotStructured() = result === NoOutputMark
+
+ override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
+ encodePolymorphically(serializer, value) {
+ polymorphicDiscriminator = it
+ }
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+ // we currently do not structures as map key
+ if (currentElementIsMapKey) {
+ throw IllegalArgumentException(
+ "Value of type ${descriptor.serialName} can't be used in json as map key. " +
+ "It should have either primitive or enum kind, but its kind is ${descriptor.kind}."
+ )
+ }
+
+ val newMode = selectMode(descriptor)
+ if (result === NoOutputMark) {
+ result = newChild(newMode)
+ current = Node(newMode, result)
+ current.parent = current
+ } else {
+ val child = newChild(newMode)
+ current.jsObject[currentName] = child
+ enterNode(child, newMode)
+ }
+
+ if (polymorphicDiscriminator != null) {
+ current.jsObject[polymorphicDiscriminator!!] = descriptor.serialName
+ polymorphicDiscriminator = null
+ }
+
+ current.index = 0
+ return this
+ }
+
+ private fun newChild(writeMode: WriteMode) = when (writeMode) {
+ WriteMode.OBJ, WriteMode.MAP -> js("{}")
+ WriteMode.LIST -> js("[]")
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ exitNode()
+ }
+
+ @OptIn(ExperimentalSerializationApi::class)
+ fun selectMode(desc: SerialDescriptor) = when (desc.kind) {
+ StructureKind.CLASS, StructureKind.OBJECT, SerialKind.CONTEXTUAL -> WriteMode.OBJ
+ StructureKind.LIST, is PolymorphicKind -> WriteMode.LIST
+ StructureKind.MAP -> WriteMode.MAP
+ is PrimitiveKind, SerialKind.ENUM -> {
+ // the two cases are handled in DynamicObjectSerializer. But compiler does not know
+ error("DynamicObjectSerializer does not support serialization of singular primitive values or enum types.")
+ }
+ }
+}
+
+private class DynamicPrimitiveEncoder(
+ override val json: Json,
+) : AbstractEncoder(), JsonEncoder {
+
+ override val serializersModule: SerializersModule
+ get() = json.serializersModule
+
+ var result: dynamic = null
+
+ override fun encodeNull() {
+ result = null
+ }
+
+ override fun encodeLong(value: Long) {
+ val asDouble = value.toDouble()
+ // todo: shall it be driven by isLenient or another configuration key?
+ if (!json.configuration.isLenient && abs(value) > MAX_SAFE_INTEGER) {
+ throw IllegalArgumentException(
+ "$value can't be deserialized to number due to a potential precision loss. " +
+ "Use the JsonConfiguration option isLenient to serialise anyway"
+ )
+ }
+ encodeValue(asDouble)
+ }
+
+ override fun encodeChar(value: Char) {
+ encodeValue(value.toString())
+ }
+
+ override fun encodeValue(value: Any) {
+ result = value
+ }
+
+ @OptIn(ExperimentalSerializationApi::class)
+ override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) {
+ encodeValue(enumDescriptor.getElementName(index))
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ }
+
+ override fun encodeJsonElement(element: JsonElement) {
+ encodeSerializableValue(JsonElementSerializer, element)
+ }
+}
diff --git a/formats/json/jsMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt b/formats/json/jsMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt
new file mode 100644
index 00000000..1b79e27e
--- /dev/null
+++ b/formats/json/jsMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt
@@ -0,0 +1,28 @@
+package kotlinx.serialization.json.internal
+
+internal actual class JsonStringBuilder actual constructor() {
+ private val sb = StringBuilder(128)
+
+ actual fun append(value: Long) {
+ sb.append(value)
+ }
+
+ actual fun append(ch: Char) {
+ sb.append(ch)
+ }
+
+ actual fun append(string: String) {
+ sb.append(string)
+ }
+
+ actual fun appendQuoted(string: String) {
+ sb.printQuoted(string)
+ }
+
+ actual override fun toString(): String {
+ return sb.toString()
+ }
+
+ actual fun release() {
+ }
+}
diff --git a/formats/json/jsMain/src/kotlinx/serialization/json/internal/createMapForCache.kt b/formats/json/jsMain/src/kotlinx/serialization/json/internal/createMapForCache.kt
new file mode 100644
index 00000000..b51ff401
--- /dev/null
+++ b/formats/json/jsMain/src/kotlinx/serialization/json/internal/createMapForCache.kt
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+/**
+ * Creates a ConcurrentHashMap on JVM and regular HashMap on other platforms.
+ * To make actual use of cache in Kotlin/Native, mark a top-level object with this map
+ * as a @[ThreadLocal].
+ */
+internal actual fun <K, V> createMapForCache(initialCapacity: Int): MutableMap<K, V> = HashMap(initialCapacity)
diff --git a/formats/json/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicSpecialCasesTest.kt b/formats/json/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicSpecialCasesTest.kt
new file mode 100644
index 00000000..748a2dce
--- /dev/null
+++ b/formats/json/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicSpecialCasesTest.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class DecodeFromDynamicSpecialCasesTest {
+
+ @Test
+ fun testTopLevelInt() {
+ val dyn = js("42")
+ val parsed = Json.decodeFromDynamic<Int>(dyn)
+ assertEquals(42, parsed)
+ }
+
+ @Test
+ fun testTopLevelString() {
+ val dyn = js(""""42"""")
+ val parsed = Json.decodeFromDynamic<String>(dyn)
+ assertEquals("42", parsed)
+ }
+
+ @Test
+ fun testTopLevelList() {
+ val dyn = js("[1, 2, 3]")
+ val parsed = Json.decodeFromDynamic<List<Int>>(dyn)
+ assertEquals(listOf(1, 2, 3), parsed)
+ }
+
+ @Test
+ fun testStringMap() = testMapWithPrimitiveKey("1", "2")
+
+ @Test
+ fun testByteMap() = testMapWithPrimitiveKey(1.toByte(), 2.toByte())
+
+ @Test
+ fun testCharMap() = testMapWithPrimitiveKey('1', '2')
+
+ @Test
+ fun testShortMap() = testMapWithPrimitiveKey(1.toShort(), 2.toShort())
+
+ @Test
+ fun testIntMap() = testMapWithPrimitiveKey(1, 2)
+
+ @Test
+ fun testLongMap() = testMapWithPrimitiveKey(1L, 2L)
+
+ @Test
+ fun testDoubleMap() = testMapWithPrimitiveKey(1.0, 2.0)
+
+ @Test
+ fun testFloatMap() = testMapWithPrimitiveKey(1.0f, 2.0f)
+
+ private inline fun <reified T> testMapWithPrimitiveKey(k1: T, k2: T) {
+ val map = mapOf(k1 to 3, k2 to 4)
+ val dyn = js("{1:3, 2:4}")
+ val parsed = Json.decodeFromDynamic<Map<T, Int>>(dyn)
+ assertEquals(map, parsed)
+ }
+
+ @Test
+ fun testJsonPrimitive() {
+ testJsonElement<JsonPrimitive>(js("42"), JsonPrimitive(42))
+ testJsonElement<JsonElement>(js("42"), JsonPrimitive(42))
+ }
+
+ @Test
+ fun testJsonPrimitiveDouble() {
+ testJsonElement<JsonElement>(js("42.0"), JsonPrimitive(42.0))
+ testJsonElement<JsonPrimitive>(js("42.0"), JsonPrimitive(42.0))
+ }
+
+ @Test
+ fun testJsonStringPrimitive() {
+ testJsonElement<JsonElement>(js(""""42""""), JsonPrimitive("42"))
+ testJsonElement<JsonPrimitive>(js(""""42""""), JsonPrimitive("42"))
+ }
+
+ @Test
+ fun testJsonNull() {
+ testJsonElement<JsonElement>(js("null"), JsonNull)
+ testJsonElement<JsonElement>(js("undefined"), JsonNull)
+ }
+
+ @Test
+ fun testJsonArray() {
+ testJsonElement<JsonElement>(js("[1,2,3]"), JsonArray((1..3).map(::JsonPrimitive)))
+ testJsonElement<JsonArray>(js("[1,2,3]"), JsonArray((1..3).map(::JsonPrimitive)))
+ }
+
+ @Test
+ fun testJsonObject() {
+ testJsonElement<JsonElement>(
+ js("""{1:2,"3":4}"""),
+ JsonObject(mapOf("1" to JsonPrimitive(2), "3" to JsonPrimitive(4)))
+ )
+ testJsonElement<JsonObject>(
+ js("""{1:2,"3":4}"""),
+ JsonObject(mapOf("1" to JsonPrimitive(2), "3" to JsonPrimitive(4)))
+ )
+ }
+
+ private inline fun <reified T : JsonElement> testJsonElement(js: dynamic, expected: JsonElement) {
+ val parsed = Json.decodeFromDynamic<T>(js)
+ assertEquals(expected, parsed)
+ }
+
+ @Serializable
+ data class Wrapper(val e: JsonElement, val p: JsonPrimitive, val o: JsonObject, val a: JsonArray, val n: JsonNull)
+
+ @Test
+ fun testJsonElementWrapper() {
+ val js = js("""{"e":42,"p":"239", "o":{"k":"v"}, "a":[1, 2, 3], "n": null}""")
+ val parsed = Json.decodeFromDynamic<Wrapper>(js)
+ val expected = Wrapper(JsonPrimitive(42), JsonPrimitive("239"), buildJsonObject { put("k", "v") }, JsonArray((1..3).map(::JsonPrimitive)), JsonNull)
+ assertEquals(expected, parsed)
+ }
+}
diff --git a/formats/json/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicTest.kt b/formats/json/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicTest.kt
new file mode 100644
index 00000000..1a0c29c2
--- /dev/null
+++ b/formats/json/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicTest.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+import kotlin.test.assertFailsWith
+
+class DecodeFromDynamicTest {
+ @Serializable
+ data class Data(val a: Int)
+
+ @Serializable
+ data class DataWrapper(val s: String, val d: Data?)
+
+ @Serializable
+ data class DataWrapperOptional(val s: String,val d: Data? = null)
+
+ @Serializable
+ data class IntList(val l: List<Int>)
+
+ @Serializable
+ data class ListOfLists(val l: List<List<Data>>)
+
+ @Serializable
+ data class MapWrapper(val m: Map<String, Int>)
+
+ @Serializable
+ data class ComplexMapWrapper(val m: Map<String, Data>)
+
+ @Serializable
+ data class IntMapWrapper(val m: Map<Int, Int>)
+
+ @Serializable
+ data class WithChar(val a: Char)
+
+ @Serializable
+ data class AllTypes(
+ val b: Byte,
+ val s: Short,
+ val i: Int,
+ val f: Float,
+ val d: Double,
+ val c: Char,
+ val B: Boolean,
+ val S: String
+ )
+
+ @Serializable
+ data class NonTrivialMap(val m: Map<String, Char>)
+
+ data class NotDefault(val a: Int)
+
+ object NDSerializer : KSerializer<NotDefault> {
+ override val descriptor = buildClassSerialDescriptor("notDefault") {
+ element<Int>("a")
+ }
+
+ override fun serialize(encoder: Encoder, value: NotDefault) {
+ encoder.encodeInt(value.a)
+ }
+
+ override fun deserialize(decoder: Decoder): NotDefault {
+ return NotDefault(decoder.decodeInt())
+ }
+ }
+
+ @Serializable
+ data class NDWrapper(@Contextual val data: NotDefault)
+
+ @Test
+ fun dynamicSimpleTest() {
+ val dyn = js("{a: 42}")
+ val parsed = Json.decodeFromDynamic(Data.serializer(), dyn)
+ assertEquals(Data(42), parsed)
+
+ val dyn2 = js("{a: 'a'}")
+ val parsed2 = Json.decodeFromDynamic(WithChar.serializer(), dyn2)
+ assertEquals(WithChar('a'), parsed2)
+
+ val dyn3 = js("{a: 97}")
+ val parsed3 = Json.decodeFromDynamic(WithChar.serializer(), dyn3)
+ assertEquals(WithChar('a'), parsed3)
+ }
+
+ @Test
+ fun dynamicAllTypesTest() {
+ val dyn = js("""{ b: 1, s: 2, i: 3, f: 1.0, d: 42.0, c: 'a', B: true, S: "str"}""")
+ val kotlinObj = AllTypes(1, 2, 3, 1.0f, 42.0, 'a', true, "str")
+
+ assertEquals(kotlinObj, Json.decodeFromDynamic(AllTypes.serializer(), dyn))
+ }
+
+ @Test
+ fun dynamicNestedTest() {
+ val dyn = js("""{s:"foo", d:{a:42}}""")
+ val parsed = Json.decodeFromDynamic(DataWrapper.serializer(), dyn)
+ val expected = DataWrapper("foo", Data(42))
+ assertEquals(expected, parsed)
+ assertEquals(3, parsed.s.length)
+ assertFailsWith(ClassCastException::class) { dyn as DataWrapper }
+ }
+
+ @Test
+ fun dynamicNullableTest() {
+ val dyn1 = js("""({s:"foo", d: null})""")
+ val dyn2 = js("""({s:"foo"})""")
+ val dyn3 = js("""({s:"foo", d: undefined})""")
+
+ assertEquals(DataWrapper("foo", null), Json.decodeFromDynamic(DataWrapper.serializer(), dyn1))
+ assertFailsWithMissingField {
+ Json.decodeFromDynamic(
+ DataWrapper.serializer(),
+ dyn2
+ )
+ }
+ assertFailsWithMissingField {
+ Json.decodeFromDynamic(
+ DataWrapper.serializer(),
+ dyn3
+ )
+ }
+ }
+
+ @Test
+ fun dynamicOptionalTest() {
+ val dyn1 = js("""({s:"foo", d: null})""")
+ val dyn2 = js("""({s:"foo"})""")
+ val dyn3 = js("""({s:"foo", d: undefined})""")
+
+ assertEquals(
+ DataWrapperOptional("foo", null),
+ Json.decodeFromDynamic(DataWrapperOptional.serializer(), dyn1)
+ )
+ assertEquals(
+ DataWrapperOptional("foo", null),
+ Json.decodeFromDynamic(DataWrapperOptional.serializer(), dyn2)
+ )
+ assertEquals(
+ DataWrapperOptional("foo", null),
+ Json.decodeFromDynamic(DataWrapperOptional.serializer(), dyn3)
+ )
+ }
+
+ @Test
+ fun dynamicListTest() {
+ val dyn1 = js("""({l:[1,2]})""")
+ val dyn2 = js("""({l:[[],[{a:42}]]})""")
+
+ assertEquals(IntList(listOf(1, 2)), Json.decodeFromDynamic(IntList.serializer(), dyn1))
+ assertEquals(
+ ListOfLists(listOf(listOf(), listOf(Data(42)))),
+ Json.decodeFromDynamic(ListOfLists.serializer(), dyn2)
+ )
+ }
+
+ @Test
+ fun dynamicMapTest() {
+ val dyn = js("({m : {\"a\": 1, \"b\" : 2}})")
+ val m = MapWrapper(mapOf("a" to 1, "b" to 2))
+ assertEquals(m, Json.decodeFromDynamic(MapWrapper.serializer(), dyn))
+ }
+
+ @Test
+ fun testFunnyMap() {
+ val dyn = js("({m: {\"a\": 'b', \"b\" : 'a'}})")
+ val m = NonTrivialMap(mapOf("a" to 'b', "b" to 'a'))
+ assertEquals(m, Json.decodeFromDynamic(NonTrivialMap.serializer(), dyn))
+ }
+
+ @Test
+ fun dynamicMapComplexTest() {
+ val dyn = js("({m: {1: {a: 42}, 2: {a: 43}}})")
+ val m = ComplexMapWrapper(mapOf("1" to Data(42), "2" to Data(43)))
+ assertEquals(m, Json.decodeFromDynamic(ComplexMapWrapper.serializer(), dyn))
+ }
+
+ @Test
+ fun testIntMapTest() {
+ val dyn = js("({m: {1: 2, 3: 4}})")
+ val m = IntMapWrapper(mapOf(1 to 2, 3 to 4))
+ assertEquals(m, Json.decodeFromDynamic(IntMapWrapper.serializer(), dyn))
+ }
+
+ @Test
+ fun parseWithCustomSerializers() {
+ val json = Json { serializersModule = serializersModuleOf(NotDefault::class, NDSerializer) }
+ val dyn1 = js("({data: 42})")
+ assertEquals(NDWrapper(NotDefault(42)),
+ json.decodeFromDynamic(NDWrapper.serializer(), dyn1)
+ )
+ }
+
+}
diff --git a/formats/json/jsTest/src/kotlinx/serialization/json/DynamicPolymorphismTest.kt b/formats/json/jsTest/src/kotlinx/serialization/json/DynamicPolymorphismTest.kt
new file mode 100644
index 00000000..0af00c66
--- /dev/null
+++ b/formats/json/jsTest/src/kotlinx/serialization/json/DynamicPolymorphismTest.kt
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.modules.SerializersModule
+import kotlinx.serialization.modules.polymorphic
+import kotlinx.serialization.modules.subclass
+import kotlinx.serialization.test.noLegacyJs
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class DynamicPolymorphismTest {
+ @Serializable
+ sealed class Sealed(val intField: Int) {
+ @Serializable
+ @SerialName("object")
+ object ObjectChild : Sealed(0)
+
+ @Serializable
+ @SerialName("data_class")
+ data class DataClassChild(val name: String) : Sealed(1)
+
+ @Serializable
+ @SerialName("type_child")
+ data class TypeChild(val type: String) : Sealed(2)
+
+ @Serializable
+ @SerialName("nullable_child")
+ data class NullableChild(val nullable: String?): Sealed(3)
+
+ @Serializable
+ @SerialName("list_child")
+ data class ListChild(val list: List<String>): Sealed(4)
+
+ @Serializable
+ @SerialName("default_child")
+ data class DefaultChild(val default: String? = "default"): Sealed(5)
+ }
+
+ @Serializable
+ @JsonClassDiscriminator("sealed_custom")
+ sealed class SealedCustom {
+ @Serializable
+ @SerialName("data_class")
+ data class DataClassChild(val name: String) : SealedCustom()
+ }
+
+ @Serializable
+ data class CompositeClass(val mark: String, val nested: Sealed)
+
+ @Serializable
+ data class AnyWrapper(@Polymorphic val any: Any)
+
+ @Serializable
+ @SerialName("string_wrapper")
+ data class StringWrapper(val text: String)
+
+ private val arrayJson = Json {
+ useArrayPolymorphism = true
+ }
+
+ private val objectJson = Json {
+ useArrayPolymorphism = false
+ }
+
+ @Test
+ fun testDiscriminatorName() {
+ val newClassDiscriminator = "key"
+
+ val json = Json {
+ useArrayPolymorphism = false
+ classDiscriminator = newClassDiscriminator
+ }
+
+ val value = Sealed.TypeChild("discriminator-test")
+ encodeAndDecode(Sealed.serializer(), value, json) {
+ assertEquals("type_child", this[newClassDiscriminator])
+ assertEquals(value.type, this.type)
+ assertEquals(value.intField, this.intField)
+ assertEquals(3, fieldsCount(this))
+ }
+ }
+
+ @Test
+ fun testCustomClassDiscriminator() = noLegacyJs {
+ val value = SealedCustom.DataClassChild("custom-discriminator-test")
+ encodeAndDecode(SealedCustom.serializer(), value, objectJson) {
+ assertEquals("data_class", this["sealed_custom"])
+ assertEquals(undefined, this.type)
+ assertEquals(2, fieldsCount(this))
+ }
+ }
+
+ @Test
+ fun testComposite() {
+ val nestedValue = Sealed.DataClassChild("child")
+ val value = CompositeClass("composite", nestedValue)
+ encodeAndDecode(CompositeClass.serializer(), value, objectJson) {
+ assertEquals(value.mark, this.mark)
+ val nested = this.nested
+ assertEquals("data_class", nested.type)
+ assertEquals(nestedValue.name, nested.name)
+ assertEquals(nestedValue.intField, nested.intField)
+ assertEquals(3, fieldsCount(nested))
+ }
+
+ encodeAndDecode(CompositeClass.serializer(), value, arrayJson) {
+ assertEquals(value.mark, this.mark)
+ assertEquals("data_class", this.nested[0])
+ val nested = this.nested[1]
+ assertEquals(nestedValue.name, nested.name)
+ assertEquals(nestedValue.intField, nested.intField)
+ assertEquals(2, fieldsCount(nested))
+ }
+ }
+
+
+ @Test
+ fun testDataClass() {
+ val value = Sealed.DataClassChild("data-class")
+
+ encodeAndDecode(Sealed.serializer(), value, objectJson) {
+ assertEquals("data_class", this.type)
+ assertEquals(value.name, this.name)
+ assertEquals(value.intField, this.intField)
+ assertEquals(3, fieldsCount(this))
+ }
+
+ encodeAndDecode(Sealed.serializer(), value, arrayJson) {
+ assertEquals("data_class", this[0])
+ val dynamicValue = this[1]
+ assertEquals(value.name, dynamicValue.name)
+ assertEquals(value.intField, dynamicValue.intField)
+ assertEquals(2, fieldsCount(dynamicValue))
+ }
+ }
+
+ @Test
+ fun testNullable() {
+ val nonNullChild = Sealed.NullableChild("nonnull")
+ encodeAndDecode(Sealed.serializer(), nonNullChild, arrayJson) {
+ assertEquals("nullable_child", this[0])
+ val dynamicValue = this[1]
+ assertEquals(nonNullChild.nullable, dynamicValue.nullable)
+ assertEquals(nonNullChild.intField, dynamicValue.intField)
+ assertEquals(2, fieldsCount(dynamicValue))
+ }
+ encodeAndDecode(Sealed.serializer(), nonNullChild, objectJson) {
+ assertEquals("nullable_child", this.type)
+ assertEquals(nonNullChild.nullable, this.nullable)
+ assertEquals(nonNullChild.intField, this.intField)
+ assertEquals(3, fieldsCount(this))
+ }
+
+ val nullChild = Sealed.NullableChild(null)
+ encodeAndDecode(Sealed.serializer(), nullChild, arrayJson) {
+ assertEquals("nullable_child", this[0])
+ val dynamicValue = this[1]
+ assertEquals(nullChild.nullable, dynamicValue.nullable)
+ assertEquals(nullChild.intField, dynamicValue.intField)
+ assertEquals(2, fieldsCount(dynamicValue))
+ }
+ encodeAndDecode(Sealed.serializer(), nullChild, objectJson) {
+ assertEquals("nullable_child", this.type)
+ assertEquals(nullChild.nullable, this.nullable)
+ assertEquals(nullChild.intField, this.intField)
+ assertEquals(3, fieldsCount(this))
+ }
+ }
+
+ @Test
+ fun testList() {
+ val listChild = Sealed.ListChild(listOf("one", "two"))
+ encodeAndDecode(Sealed.serializer(), listChild, arrayJson) {
+ assertEquals("list_child", this[0])
+ val dynamicValue = this[1]
+ assertEquals(listChild.list, (dynamicValue.list as Array<String>).toList())
+ assertEquals(listChild.intField, dynamicValue.intField)
+ assertEquals(2, fieldsCount(dynamicValue))
+ }
+ encodeAndDecode(Sealed.serializer(), listChild, objectJson) {
+ assertEquals("list_child", this.type)
+ assertEquals(listChild.list, (this.list as Array<String>).toList())
+ assertEquals(listChild.intField, this.intField)
+ assertEquals(3, fieldsCount(this))
+ }
+ }
+
+ @Test
+ fun testEmptyList() {
+ val emptyListChild = Sealed.ListChild(emptyList())
+ encodeAndDecode(Sealed.serializer(), emptyListChild, arrayJson) {
+ assertEquals("list_child", this[0])
+ val dynamicValue = this[1]
+ assertEquals(emptyListChild.list, (dynamicValue.list as Array<String>).toList())
+ assertEquals(emptyListChild.intField, dynamicValue.intField)
+ assertEquals(2, fieldsCount(dynamicValue))
+ }
+ encodeAndDecode(Sealed.serializer(), emptyListChild, objectJson) {
+ assertEquals("list_child", this.type)
+ assertEquals(emptyListChild.list, (this.list as Array<String>).toList())
+ assertEquals(emptyListChild.intField, this.intField)
+ assertEquals(3, fieldsCount(this))
+ }
+ }
+
+ @Test
+ fun testDefaultValue() {
+ val objectJsonWithDefaults = Json(objectJson) {
+ encodeDefaults = true
+ }
+
+ val arrayJsonWithDefaults = Json(arrayJson) {
+ encodeDefaults = true
+ }
+
+ val defaultChild = Sealed.DefaultChild()
+ encodeAndDecode(Sealed.serializer(), defaultChild, arrayJson) {
+ assertEquals("default_child", this[0])
+ val dynamicValue = this[1]
+ assertEquals(null, dynamicValue.default, "arrayJson should not encode defaults")
+ assertEquals(defaultChild.intField, dynamicValue.intField)
+ assertEquals(1, fieldsCount(dynamicValue))
+ }
+ encodeAndDecode(Sealed.serializer(), defaultChild, arrayJsonWithDefaults) {
+ assertEquals("default_child", this[0])
+ val dynamicValue = this[1]
+ assertEquals(defaultChild.default, dynamicValue.default, "arrayJsonWithDefaults should encode defaults")
+ assertEquals(defaultChild.intField, dynamicValue.intField)
+ assertEquals(2, fieldsCount(dynamicValue))
+ }
+
+ encodeAndDecode(Sealed.serializer(), defaultChild, objectJson) {
+ assertEquals("default_child", this.type)
+ assertEquals(null, this.default, "objectJson should not encode defaults")
+ assertEquals(defaultChild.intField, this.intField)
+ assertEquals(2, fieldsCount(this))
+ }
+ encodeAndDecode(Sealed.serializer(), defaultChild, objectJsonWithDefaults) {
+ assertEquals("default_child", this.type)
+ assertEquals(defaultChild.default, this.default, "objectJsonWithDefaults should encode defaults")
+ assertEquals(defaultChild.intField, this.intField)
+ assertEquals(3, fieldsCount(this))
+ }
+
+ }
+
+ @Test
+ fun testNonDefaultValue() {
+ val nonDefaultChild = Sealed.DefaultChild("non default value")
+ encodeAndDecode(Sealed.serializer(), nonDefaultChild, arrayJson) {
+ assertEquals("default_child", this[0])
+ val dynamicValue = this[1]
+ assertEquals(nonDefaultChild.default, dynamicValue.default)
+ assertEquals(nonDefaultChild.intField, dynamicValue.intField)
+ assertEquals(2, fieldsCount(dynamicValue))
+ }
+
+ encodeAndDecode(Sealed.serializer(), nonDefaultChild, objectJson) {
+ assertEquals("default_child", this.type)
+ assertEquals(nonDefaultChild.default, this.default)
+ assertEquals(nonDefaultChild.intField, this.intField)
+ assertEquals(3, fieldsCount(this))
+ }
+ }
+
+ @Test
+ fun testObject() {
+ val value = Sealed.ObjectChild
+ encodeAndDecode(Sealed.serializer(), value, objectJson) {
+ assertEquals("object", this.type)
+ assertEquals(1, fieldsCount(this))
+ }
+
+ encodeAndDecode(Sealed.serializer(), value, arrayJson) {
+ assertEquals("object", this[0])
+ assertEquals(0, fieldsCount(this[1]))
+ }
+ }
+
+ @Test
+ fun testAny() {
+ val serializersModule = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(StringWrapper.serializer())
+ }
+ }
+
+ val json = Json(objectJson) { this.serializersModule = serializersModule }
+
+ val anyValue = StringWrapper("some text")
+ val value = AnyWrapper(anyValue)
+
+ encodeAndDecode(AnyWrapper.serializer(), value, json) {
+ assertEquals("string_wrapper", this.any.type)
+ assertEquals(anyValue.text, this.any.text)
+ assertEquals(1, fieldsCount(this))
+ assertEquals(2, fieldsCount(this.any))
+ }
+
+ val json2 = Json(arrayJson) { this.serializersModule = serializersModule }
+
+ encodeAndDecode(AnyWrapper.serializer(), value, json2) {
+ assertEquals("string_wrapper", this.any[0])
+ assertEquals(anyValue.text, this.any[1].text)
+ assertEquals(1, fieldsCount(this))
+ assertEquals(2, fieldsCount(this.any))
+ }
+ }
+
+ @Suppress("NOTHING_TO_INLINE")
+ private inline fun fieldsCount(dynamic: dynamic): Int {
+ return js("Object").keys(dynamic).length as Int
+ }
+
+ private fun <T> encodeAndDecode(deserializer: KSerializer<T>, value: T, json: Json, assertBlock: dynamic.() -> Unit) {
+ val dynamic = json.encodeToDynamic(deserializer, value)
+ assertBlock(dynamic)
+ val decodedValue = json.decodeFromDynamic(deserializer, dynamic)
+ assertEquals(value, decodedValue)
+ }
+}
diff --git a/formats/json/jsTest/src/kotlinx/serialization/json/DynamicToLongTest.kt b/formats/json/jsTest/src/kotlinx/serialization/json/DynamicToLongTest.kt
new file mode 100644
index 00000000..9c5011a8
--- /dev/null
+++ b/formats/json/jsTest/src/kotlinx/serialization/json/DynamicToLongTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class DynamicToLongTest {
+
+ @Serializable
+ data class HasLong(val l: Long)
+
+ private fun test(dynamic: dynamic, expectedResult: Result<Long>) {
+ val parsed = kotlin.runCatching { Json.decodeFromDynamic(HasLong.serializer(), dynamic).l }
+ assertEquals(expectedResult.isSuccess, parsed.isSuccess, "Results are different")
+ parsed.onSuccess { assertEquals(expectedResult.getOrThrow(), it) }
+ // to compare without message
+ parsed.onFailure { assertSame(expectedResult.exceptionOrNull()!!::class, it::class) }
+ }
+
+ private fun shouldFail(dynamic: dynamic) = test(dynamic, Result.failure(SerializationException("")))
+
+ @Test
+ fun canParseNotSoBigLongs() {
+ test(js("{l:1}"), Result.success(1))
+ test(js("{l:0}"), Result.success(0))
+ test(js("{l:-1}"), Result.success(-1))
+ }
+
+ @Test
+ fun ignoresIncorrectValues() {
+ shouldFail(js("{l:0.5}"))
+ shouldFail(js("{l: Math.PI}"))
+ shouldFail(js("{l: NaN}"))
+ shouldFail(js("""{l: "a string"}"""))
+ shouldFail(js("{l:Infinity}"))
+ shouldFail(js("{l:+Infinity}"))
+ shouldFail(js("{l:-Infinity}"))
+ }
+
+ @Test
+ fun handlesEdgyValues() {
+ test(js("{l:Number.MAX_SAFE_INTEGER}"), Result.success(MAX_SAFE_INTEGER.toLong()))
+ test(js("{l:Number.MAX_SAFE_INTEGER - 1}"), Result.success(MAX_SAFE_INTEGER.toLong() - 1))
+ test(js("{l:-Number.MAX_SAFE_INTEGER}"), Result.success(-MAX_SAFE_INTEGER.toLong()))
+ shouldFail(js("{l: Number.MAX_SAFE_INTEGER + 1}"))
+ shouldFail(js("{l: Number.MAX_SAFE_INTEGER + 2}"))
+ shouldFail(js("{l: -Number.MAX_SAFE_INTEGER - 1}"))
+ shouldFail(js("{l: 2e100}"))
+ shouldFail(js("{l: 2e100 + 1}"))
+ test(js("{l: Math.pow(2, 53) - 1}"), Result.success(MAX_SAFE_INTEGER.toLong()))
+ }
+}
diff --git a/formats/json/jsTest/src/kotlinx/serialization/json/EncodeToDynamicSpecialCasesTest.kt b/formats/json/jsTest/src/kotlinx/serialization/json/EncodeToDynamicSpecialCasesTest.kt
new file mode 100644
index 00000000..ead18969
--- /dev/null
+++ b/formats/json/jsTest/src/kotlinx/serialization/json/EncodeToDynamicSpecialCasesTest.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+import kotlin.test.assertFailsWith
+
+class EncodeToDynamicSpecialCasesTest {
+
+ @Test
+ fun testTopLevelInt() = assertDynamicForm(42)
+
+ @Test
+ fun testTopLevelString() = assertDynamicForm("42")
+
+ @Test
+ fun testTopLevelList() = assertDynamicForm(listOf(1, 2, 3))
+
+ @Test
+ fun testStringMap() = assertDynamicForm(mapOf("1" to 2, "3" to 4))
+
+ @Test
+ fun testByteMap() = assertDynamicForm(mapOf(1.toByte() to 2, 3.toByte() to 4))
+
+ @Test
+ fun testCharMap() = assertDynamicForm(mapOf('1' to 2, '3' to 4))
+
+ @Test
+ fun testShortMap() = assertDynamicForm(mapOf(1.toShort() to 2, 3.toShort() to 4))
+
+ @Test
+ fun testIntMap() = assertDynamicForm(mapOf(1 to 2, 3 to 4))
+
+ @Test
+ fun testLongMap() = assertDynamicForm(mapOf(1L to 2, 3L to 4))
+
+ @Test
+ fun testDoubleMap() = assertDynamicForm(mapOf(1.0 to 2, 3.0 to 4))
+
+ @Test
+ fun testFloatMap() = assertDynamicForm(mapOf(1.0f to 2, 3.0f to 4))
+
+ @Test
+ fun testJsonPrimitive() {
+ assertDynamicForm(JsonPrimitive(42))
+ assertDynamicForm<JsonElement>(JsonPrimitive(42))
+ }
+
+ @Test
+ fun testJsonPrimitiveDouble() {
+ assertDynamicForm<JsonElement>(JsonPrimitive(42.0))
+ assertDynamicForm<JsonPrimitive>(JsonPrimitive(42.0))
+ }
+
+ @Test
+ fun testJsonStringPrimitive() {
+ assertDynamicForm<JsonElement>(JsonPrimitive("42"))
+ assertDynamicForm<JsonPrimitive>(JsonPrimitive("42"))
+ }
+
+ @Test
+ fun testJsonArray() {
+ assertDynamicForm<JsonElement>(JsonArray((1..3).map(::JsonPrimitive)))
+ assertDynamicForm<JsonArray>(JsonArray((1..3).map(::JsonPrimitive)))
+ }
+
+ @Test
+ fun testJsonObject() {
+ assertDynamicForm<JsonElement>(
+ JsonObject(mapOf("1" to JsonPrimitive(2), "3" to JsonPrimitive(4)))
+ )
+ assertDynamicForm<JsonObject>(
+ JsonObject(mapOf("1" to JsonPrimitive(2), "3" to JsonPrimitive(4)))
+ )
+ }
+
+
+ @Serializable
+ data class Wrapper(val e: JsonElement, val p: JsonPrimitive, val o: JsonObject, val a: JsonArray)
+
+ @Test
+ fun testJsonElementWrapper() {
+ assertDynamicForm(Wrapper(JsonPrimitive(42), JsonPrimitive("239"), buildJsonObject { put("k", "v") }, JsonArray((1..3).map(::JsonPrimitive))))
+ }
+}
diff --git a/formats/json/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt b/formats/json/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt
new file mode 100644
index 00000000..1c3c24c7
--- /dev/null
+++ b/formats/json/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+@Suppress("UnsafeCastFromDynamic")
+class EncodeToDynamicTest {
+ @Serializable
+ data class Data(val a: Int)
+
+ @Serializable
+ open class DataWrapper(open val s: String, val d: Data? = Data(1)) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class.js != other::class.js) return false
+
+ other as DataWrapper
+
+ if (s != other.s) return false
+ if (d != other.d) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = s.hashCode()
+ result = 31 * result + (d?.hashCode() ?: 0)
+ return result
+ }
+ }
+
+ @Serializable
+ data class NestedList(val a: String, val list: List<Int>)
+
+ @Serializable
+ data class ListOfLists(val l: List<List<Data>>)
+
+ @Serializable
+ data class MapWrapper(val m: Map<String?, Int>)
+
+ @Serializable
+ data class ComplexMapWrapper(val m: Map<String, Data>)
+
+ @Serializable
+ data class WithChar(val a: Char)
+
+ @Serializable
+ data class WithLong(val l: Long)
+
+ @Serializable
+ data class AllTypes(
+ val b: Byte,
+ val s: Short,
+ val i: Int,
+ val f: Float,
+ val d: Double,
+ val c: Char,
+ val B: Boolean,
+ val S: String
+ )
+
+ @Serializable
+ data class EnumWrapper(val e: Color)
+
+ @Serializable
+ sealed class Sealed {
+ @Serializable
+ data class One(val string: String) : Sealed()
+ }
+
+ @Serializable
+ class WithJsName(@JsName("b") val a: String)
+
+ @Serializable
+ data class WithSerialName(@SerialName("b") val a: String)
+
+ @Serializable
+ enum class Color {
+ RED,
+ GREEN,
+
+ @SerialName("red")
+ WITH_SERIALNAME_red
+ }
+
+ @Serializable
+ data class MyFancyClass(val value: String) {
+
+ @Serializer(forClass = MyFancyClass::class)
+ companion object : KSerializer<MyFancyClass> {
+
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MyFancyClass", PrimitiveKind.STRING)
+ override fun serialize(encoder: Encoder, value: MyFancyClass) {
+ encoder.encodeString("fancy ${value.value}")
+ }
+
+ override fun deserialize(decoder: Decoder): MyFancyClass {
+ return MyFancyClass(decoder.decodeString().removePrefix("fancy "))
+ }
+ }
+ }
+
+ @Test
+ fun dynamicSimpleTest() {
+ assertDynamicForm(Data(42)) { data, serialized ->
+ assertEquals(data.a, serialized.a)
+ }
+
+ assertDynamicForm(WithChar('c')) { data, serialized ->
+ assertEquals(data.a.toString(), serialized.a)
+ }
+
+ assertDynamicForm(AllTypes(1, 2, 3, 4.0f, 5.0, 'c', true, "string"))
+
+
+ assertDynamicForm(WithLong(5L))
+ assertDynamicForm(WithLong(MAX_SAFE_INTEGER.toLong()))
+ assertDynamicForm(WithLong(MAX_SAFE_INTEGER.unaryMinus().toLong()))
+ }
+
+ @Test
+ fun wrappedObjectsTest() {
+ assertDynamicForm(DataWrapper("a string", Data(42))) { data, serialized ->
+ assertEquals(data.s, serialized.s)
+ assertNotNull(serialized.d)
+ assertEquals(data.d?.a, serialized.d.a)
+ }
+ }
+
+ @Test
+ fun listTest() {
+ assertDynamicForm(listOf(1, 2, 3, 44), serializer = ListSerializer(Int.serializer())) { data, serialized ->
+ assertNotNull(serialized.length, "length property should exist")
+ assertEquals(data.size, serialized.length)
+
+ for (i in data.indices) {
+ assertEquals(data[i], serialized[i])
+ }
+ }
+ }
+
+ @Test
+ fun arrayTest() {
+ assertDynamicForm(intArrayOf(1, 2, 3, 44), serializer = IntArraySerializer(), true) { data, serialized ->
+ assertNotNull(serialized.length, "length property should exist")
+ assertEquals(data.size, serialized.length)
+
+ for (i in data.indices) {
+ assertEquals(data[i], serialized[i])
+ }
+ }
+ }
+
+ @Test
+ fun nestedListTest() {
+ assertDynamicForm(NestedList("a string", listOf(1, 2, 3, 44))) { data, serialized ->
+ assertEquals(data.a, serialized.a)
+ assertNotNull(serialized.list.length, "length property should exist")
+ assertEquals(data.list.size, serialized.list.length)
+
+ for (i in data.list.indices) {
+ assertEquals(data.list[i], serialized.list[i])
+ }
+ }
+
+ }
+
+ @Test
+ fun complexMapWrapperTest() {
+ assertDynamicForm(ComplexMapWrapper(mapOf("key1" to Data(1), "key2" to Data(2))))
+ }
+
+ @Test
+ fun mapWrapperTest() {
+ assertDynamicForm(MapWrapper(mapOf("key1" to 1, "key2" to 2)))
+ }
+
+ @Test
+ fun listOfListsTest() {
+ assertDynamicForm(
+ ListOfLists(
+ listOf(
+ listOf(Data(11), Data(12), Data(13)),
+ listOf(Data(21), Data(22))
+ )
+ )
+ ) { data, serialized ->
+ assertEquals(data.l.size, serialized.l.length)
+ assertEquals(data.l.first().size, serialized.l[0].length)
+ }
+ }
+
+ @Serializable
+ data class NestedCollections(val data: Map<String, Map<String, List<Int>>>)
+
+ @Test
+ fun nestedCollections() {
+ assertDynamicForm(
+ NestedCollections(
+ mapOf(
+ "one" to mapOf("oneone" to listOf(11, 12, 13), "onetwo" to listOf(1)),
+ "two" to mapOf("twotwo" to listOf(22, 23))
+ )
+ )
+ , serializer = NestedCollections.serializer()
+ )
+ }
+
+ @Test
+ fun enums() {
+ assertDynamicForm(EnumWrapper(Color.RED))
+ assertDynamicForm(Color.GREEN)
+ assertDynamicForm(Color.WITH_SERIALNAME_red) { _, serialized ->
+ assertEquals("red", serialized)
+ }
+ }
+
+ @Test
+ fun singlePrimitiveValue() {
+ assertDynamicForm("some string")
+ assertDynamicForm(1.toByte())
+ assertDynamicForm(1.toShort())
+ assertDynamicForm(1)
+ assertDynamicForm(1.toFloat())
+ assertDynamicForm(1.toDouble())
+ assertDynamicForm('c')
+ assertDynamicForm(true)
+ assertDynamicForm(false)
+ assertDynamicForm(1L)
+ val result = Json.encodeToDynamic(String.serializer().nullable, null)
+ assertEquals(null, result)
+ }
+
+ @Test
+ fun sealed() {
+ // test of sealed class but not polymorphic serialization
+ assertDynamicForm(Sealed.One("one"))
+ }
+
+ @Test
+ fun withSerialNam() {
+ assertDynamicForm(WithSerialName("something")) { data, serialized ->
+ assertEquals(data.a, serialized.b)
+ }
+ }
+
+ @Test
+ fun mapWithNullKey() {
+ val serialized = Json.encodeToDynamic(
+ MapSerializer(String.serializer().nullable, Int.serializer()),
+ mapOf(null to 0, "a" to 1)
+ )
+ assertNotNull(serialized[null], "null key should be present in output")
+ }
+
+ @Test
+ fun mapWithSimpleKey() {
+
+ inline fun <reified T> assertSimpleMapForm(key: T, value: String) {
+ assertDynamicForm(mapOf(key to value), MapSerializer(serializer(), String.serializer()))
+ }
+
+ assertSimpleMapForm(1, "Int 1")
+ assertSimpleMapForm("s", "String s")
+ assertSimpleMapForm('c', "char c")
+ assertSimpleMapForm(2.toByte(), "Byte 2")
+ assertSimpleMapForm(3.toShort(), "Short 3")
+ assertSimpleMapForm(4.toLong(), "Long 4")
+ assertSimpleMapForm(5.toFloat(), "Float 5")
+ assertSimpleMapForm(6.toDouble(), "Double 6")
+
+ assertDynamicForm(
+ mapOf(
+ Color.RED to "RED",
+ Color.GREEN to "GREEN",
+ Color.WITH_SERIALNAME_red to "red"
+ ),
+ MapSerializer(Color.serializer(), String.serializer())
+ ) { _, serialized ->
+ assertNotNull(serialized["red"], "WITH_SERIALNAME_red should be serialized as 'red'")
+ }
+ }
+
+ @Test
+ fun mapWithIllegalKey() {
+
+ val exception = assertFails {
+ Json.encodeToDynamic(
+ MapSerializer(Data.serializer(), String.serializer()),
+ mapOf(
+ Data(1) to "data",
+ Data(2) to "data",
+ Data(3) to "data"
+ )
+ )
+ }
+ assertEquals(IllegalArgumentException::class, exception::class)
+ assertTrue("should have a helpful error message") {
+ exception.message?.contains("can't be used in json as map key") == true
+ }
+
+ assertFails {
+ @Suppress("CAST_NEVER_SUCCEEDS")
+ assertDynamicForm(
+ mapOf(
+ (null as? Data) to "Data null"
+ ),
+ MapSerializer(Data.serializer().nullable, String.serializer())
+ )
+ }
+
+
+ val doubleSerializer = MapSerializer(Double.serializer(), String.serializer())
+ val value = mapOf(0.5 to "0.5")
+ var ex = assertFails {
+ assertDynamicForm(value, doubleSerializer)
+ }
+ assertTrue("should have a helpful error message") {
+ ex.message?.contains("can't be used in json as map key") == true
+ }
+
+ ex = assertFails {
+ assertDynamicForm(mapOf(Double.NaN to "NaN"), doubleSerializer)
+ }
+ assertTrue("should have a helpful error message") {
+ ex.message?.contains("can't be used in json as map key") == true
+ }
+
+ ex = assertFails {
+ assertDynamicForm(mapOf(Double.NEGATIVE_INFINITY to "NaN"), doubleSerializer)
+ }
+ assertTrue("should have a helpful error message") {
+ ex.message?.contains("can't be used in json as map key") == true
+ }
+
+ assertDynamicForm(mapOf(11.0 to "11"), doubleSerializer)
+
+ }
+
+ @Test
+ fun customSerializerTest() {
+ assertDynamicForm(MyFancyClass("apple"), MyFancyClass.serializer()) { _, serialized ->
+ assertEquals("fancy apple", serialized)
+ }
+
+ assertDynamicForm(
+ mapOf(MyFancyClass("apple") to "value"),
+ MapSerializer(MyFancyClass.serializer(), String.serializer())
+ ) { _, serialized ->
+ assertNotNull(serialized["fancy apple"], "should contain key 'fancy apple'")
+ }
+ }
+}
+
+public inline fun <reified T : Any> assertDynamicForm(
+ data: T,
+ serializer: KSerializer<T> = EmptySerializersModule.serializer(),
+ skipEqualsCheck:Boolean = false,
+ noinline assertions: ((T, dynamic) -> Unit)? = null
+) {
+ val serialized = Json.encodeToDynamic(serializer, data)
+ assertions?.invoke(data, serialized)
+ val string = Json.encodeToString(serializer, data)
+ assertEquals(
+ string,
+ JSON.stringify(serialized),
+ "JSON.stringify representation must be the same"
+ )
+
+ if (skipEqualsCheck) return // arrays etc.
+ assertEquals(data, Json.decodeFromString(serializer, string))
+}
diff --git a/formats/json/jsTest/src/kotlinx/serialization/json/JsonCoerceInputValuesDynamicTest.kt b/formats/json/jsTest/src/kotlinx/serialization/json/JsonCoerceInputValuesDynamicTest.kt
new file mode 100644
index 00000000..00053297
--- /dev/null
+++ b/formats/json/jsTest/src/kotlinx/serialization/json/JsonCoerceInputValuesDynamicTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class JsonCoerceInputValuesDynamicTest {
+ val json = Json {
+ coerceInputValues = true
+ isLenient = true
+ }
+
+ private fun <T> doTest(inputs: List<dynamic>, expected: T, serializer: KSerializer<T>) {
+ for (input in inputs) {
+ assertEquals(expected, json.decodeFromDynamic(serializer, input), "Failed on input: $input")
+ }
+ }
+
+ @Test
+ fun testUseDefaultOnNonNullableBooleanDynamic() = doTest(
+ listOf(
+ js("""{"b":false}"""),
+ js("""{"b":null}"""),
+ js("""{}"""),
+ ),
+ JsonCoerceInputValuesTest.WithBoolean(),
+ JsonCoerceInputValuesTest.WithBoolean.serializer()
+ )
+
+ @Test
+ fun testUseDefaultOnUnknownEnum() {
+ doTest(
+ listOf(
+ js("""{"e":"unknown_value"}"""),
+ js("""{"e":null}"""),
+ js("""{}"""),
+ ),
+ JsonCoerceInputValuesTest.WithEnum(),
+ JsonCoerceInputValuesTest.WithEnum.serializer()
+ )
+ assertFailsWith<SerializationException> {
+ json.decodeFromDynamic(
+ JsonCoerceInputValuesTest.WithEnum.serializer(),
+ js("""{"e":{"x":"definitely not a valid enum value"}}""")
+ )
+ }
+ }
+
+ @Test
+ fun testUseDefaultInMultipleCases() {
+ val testData = mapOf<dynamic, JsonCoerceInputValuesTest.MultipleValues>(
+ Pair(
+ js("""{"data":{"data":"foo"},"data2":null,"i":null,"e":null,"foo":"bar"}"""),
+ JsonCoerceInputValuesTest.MultipleValues(
+ StringData("foo"),
+ foo = "bar"
+ )
+ ),
+ Pair(
+ js("""{"data":{"data":"foo"},"data2":{"intV":42},"i":null,"e":null,"foo":"bar"}"""),
+ JsonCoerceInputValuesTest.MultipleValues(
+ StringData(
+ "foo"
+ ), IntData(42), foo = "bar"
+ )
+ ),
+ Pair(
+ js("""{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"NoOption","foo":"bar"}"""),
+ JsonCoerceInputValuesTest.MultipleValues(
+ StringData("foo"),
+ IntData(42),
+ i = 0,
+ foo = "bar"
+ )
+ ),
+ Pair(
+ js("""{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"OptionC","foo":"bar"}"""),
+ JsonCoerceInputValuesTest.MultipleValues(
+ StringData("foo"),
+ IntData(42),
+ i = 0,
+ e = SampleEnum.OptionC,
+ foo = "bar"
+ )
+ ),
+ )
+ for ((input, expected) in testData) {
+ assertEquals(
+ expected,
+ json.decodeFromDynamic(JsonCoerceInputValuesTest.MultipleValues.serializer(), input),
+ "Failed on input: $input"
+ )
+ }
+ }
+}
diff --git a/formats/json/jsTest/src/kotlinx/serialization/json/JsonDynamicImplicitNullsTest.kt b/formats/json/jsTest/src/kotlinx/serialization/json/JsonDynamicImplicitNullsTest.kt
new file mode 100644
index 00000000..1191e3c9
--- /dev/null
+++ b/formats/json/jsTest/src/kotlinx/serialization/json/JsonDynamicImplicitNullsTest.kt
@@ -0,0 +1,14 @@
+package kotlinx.serialization.json
+
+import kotlinx.serialization.KSerializer
+
+class JsonDynamicImplicitNullsTest : AbstractJsonImplicitNullsTest() {
+ override fun <T> Json.encode(value: T, serializer: KSerializer<T>): String {
+ return JSON.stringify(encodeToDynamic(serializer, value))
+ }
+
+ override fun <T> Json.decode(json: String, serializer: KSerializer<T>): T {
+ val x: dynamic = JSON.parse(json)
+ return decodeFromDynamic(serializer, x)
+ }
+}
diff --git a/formats/json/jsTest/src/kotlinx/serialization/json/JsonNamesDynamicTest.kt b/formats/json/jsTest/src/kotlinx/serialization/json/JsonNamesDynamicTest.kt
new file mode 100644
index 00000000..09cf3d4a
--- /dev/null
+++ b/formats/json/jsTest/src/kotlinx/serialization/json/JsonNamesDynamicTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.features.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+
+class JsonNamesDynamicTest {
+ private val inputString1 = js("""{"foo":"foo"}""")
+ private val inputString2 = js("""{"_foo":"foo"}""")
+
+ private fun parameterizedCoercingTest(test: (json: Json, msg: String) -> Unit) {
+ for (coercing in listOf(true, false)) {
+ val json = Json {
+ coerceInputValues = coercing
+ useAlternativeNames = true
+ }
+
+ test(
+ json,
+ "Failed test with coercing=$coercing"
+ )
+ }
+ }
+
+ @Test
+ fun testParsesAllAlternativeNamesDynamic() = noLegacyJs {
+ for (input in listOf(inputString1, inputString2)) {
+ parameterizedCoercingTest { json, msg ->
+ val data = json.decodeFromDynamic(JsonNamesTest.WithNames.serializer(), input)
+ assertEquals("foo", data.data, msg + "and input '$input'")
+ }
+ }
+ }
+
+ @Test
+ fun testEnumSupportsAlternativeNames() = noLegacyJs {
+ val input = js("""{"enumList":["VALUE_A", "someValue", "some_value", "VALUE_B"], "checkCoercion":"someValue"}""")
+ val expected = JsonNamesTest.WithEnumNames(
+ listOf(
+ JsonNamesTest.AlternateEnumNames.VALUE_A,
+ JsonNamesTest.AlternateEnumNames.VALUE_A,
+ JsonNamesTest.AlternateEnumNames.VALUE_A,
+ JsonNamesTest.AlternateEnumNames.VALUE_B
+ ), JsonNamesTest.AlternateEnumNames.VALUE_A
+ )
+ parameterizedCoercingTest { json, msg ->
+ assertEquals(expected, json.decodeFromDynamic(input), msg)
+ }
+ }
+
+ @Test
+ fun topLevelEnumSupportAlternativeNames() = noLegacyJs {
+ parameterizedCoercingTest { json, msg ->
+ assertEquals(JsonNamesTest.AlternateEnumNames.VALUE_A, json.decodeFromDynamic(js("\"someValue\"")), msg)
+ }
+ }
+
+ @Test
+ fun testThrowsAnErrorOnDuplicateNames2() = noLegacyJs {
+ val serializer = JsonNamesTest.CollisionWithAlternate.serializer()
+ parameterizedCoercingTest { json, _ ->
+ assertFailsWithMessage<SerializationException>(
+ """The suggested name '_foo' for property foo is already one of the names for property data""",
+ "Class ${serializer.descriptor.serialName} did not fail"
+ ) {
+ json.decodeFromDynamic(
+ serializer, inputString2,
+ )
+ }
+ }
+ }
+
+}
diff --git a/formats/json/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/formats/json/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt
new file mode 100644
index 00000000..b87276e8
--- /dev/null
+++ b/formats/json/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+public actual val currentPlatform: Platform = if (isLegacyBackend()) Platform.JS_LEGACY else Platform.JS_IR
+
+// from https://github.com/JetBrains/kotlin/blob/569187a7516e2e5ab217158a3170d4beb0c5cb5a/js/js.translator/testData/_commonFiles/testUtils.kt#L3
+private fun isLegacyBackend(): Boolean =
+ // Using eval to prevent DCE from thinking that following code depends on Kotlin module.
+ eval("(typeof Kotlin != \"undefined\" && typeof Kotlin.kotlin != \"undefined\")").unsafeCast<Boolean>()
diff --git a/formats/json/jsTest/src/kotlinx/serialization/test/JsonHelpers.kt b/formats/json/jsTest/src/kotlinx/serialization/test/JsonHelpers.kt
new file mode 100644
index 00000000..3f98c43d
--- /dev/null
+++ b/formats/json/jsTest/src/kotlinx/serialization/test/JsonHelpers.kt
@@ -0,0 +1,19 @@
+package kotlinx.serialization.test
+
+import kotlinx.serialization.DeserializationStrategy
+import kotlinx.serialization.SerializationStrategy
+import kotlinx.serialization.json.Json
+
+actual fun <T> Json.encodeViaStream(
+ serializer: SerializationStrategy<T>,
+ value: T
+): String {
+ TODO("supported on JVM only")
+}
+
+actual fun <T> Json.decodeViaStream(
+ serializer: DeserializationStrategy<T>,
+ input: String
+): T {
+ TODO("supported on JVM only")
+}
diff --git a/formats/json/jvmMain/src/kotlinx/serialization/json/JsonSchemaCache.kt b/formats/json/jvmMain/src/kotlinx/serialization/json/JsonSchemaCache.kt
new file mode 100644
index 00000000..a0820ef4
--- /dev/null
+++ b/formats/json/jvmMain/src/kotlinx/serialization/json/JsonSchemaCache.kt
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.json.internal.*
+
+@Suppress("DEPRECATION_ERROR")
+internal actual val Json.schemaCache: DescriptorSchemaCache get() = this._schemaCache
diff --git a/formats/json/jvmMain/src/kotlinx/serialization/json/JvmStreams.kt b/formats/json/jvmMain/src/kotlinx/serialization/json/JvmStreams.kt
new file mode 100644
index 00000000..3b83299c
--- /dev/null
+++ b/formats/json/jvmMain/src/kotlinx/serialization/json/JvmStreams.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.internal.*
+import java.io.*
+
+/**
+ * Serializes the [value] with [serializer] into a [stream] using JSON format and UTF-8 encoding.
+ *
+ * @throws [SerializationException] if the given value cannot be serialized to JSON.
+ * @throws [IOException] If an I/O error occurs and stream can't be written to.
+ */
+@ExperimentalSerializationApi
+public fun <T> Json.encodeToStream(
+ serializer: SerializationStrategy<T>,
+ value: T,
+ stream: OutputStream
+) {
+ val result = JsonToWriterStringBuilder(stream)
+ try {
+ val encoder = StreamingJsonEncoder(
+ result, this,
+ WriteMode.OBJ,
+ arrayOfNulls(WriteMode.values().size)
+ )
+ encoder.encodeSerializableValue(serializer, value)
+ } finally {
+ result.release()
+ }
+}
+
+/**
+ * Serializes given [value] to [stream] using UTF-8 encoding and serializer retrieved from the reified type parameter.
+ *
+ * @throws [SerializationException] if the given value cannot be serialized to JSON.
+ * @throws [IOException] If an I/O error occurs and stream can't be written to.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Json.encodeToStream(
+ value: T,
+ stream: OutputStream
+): Unit =
+ encodeToStream(serializersModule.serializer(), value, stream)
+
+/**
+ * Deserializes JSON from [stream] using UTF-8 encoding to a value of type [T] using [deserializer].
+ *
+ * Note that this functions expects that exactly one object would be present in the stream
+ * and throws an exception if there are any dangling bytes after an object.
+ *
+ * @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
+ * @throws [IOException] If an I/O error occurs and stream can't be read from.
+ */
+@ExperimentalSerializationApi
+public fun <T> Json.decodeFromStream(
+ deserializer: DeserializationStrategy<T>,
+ stream: InputStream
+): T {
+ val lexer = ReaderJsonLexer(stream)
+ val input = StreamingJsonDecoder(this, WriteMode.OBJ, lexer, deserializer.descriptor)
+ val result = input.decodeSerializableValue(deserializer)
+ lexer.expectEof()
+ return result
+}
+
+/**
+ * Deserializes the contents of given [stream] to the value of type [T] using UTF-8 encoding and
+ * deserializer retrieved from the reified type parameter.
+ *
+ * Note that this functions expects that exactly one object would be present in the stream
+ * and throws an exception if there are any dangling bytes after an object.
+ *
+ * @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
+ * @throws [IOException] If an I/O error occurs and stream can't be read from.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Json.decodeFromStream(stream: InputStream): T =
+ decodeFromStream(serializersModule.serializer(), stream)
+
+/**
+ * Description of [decodeToSequence]'s JSON input shape.
+ *
+ * The sequence represents a stream of objects parsed one by one;
+ * [DecodeSequenceMode] defines a separator between these objects.
+ * Typically, these objects are not separated by meaningful characters ([WHITESPACE_SEPARATED]),
+ * or the whole stream is a large array of objects separated with commas ([ARRAY_WRAPPED]).
+ */
+@ExperimentalSerializationApi
+public enum class DecodeSequenceMode {
+ /**
+ * Declares that objects in the input stream are separated by whitespace characters.
+ *
+ * The stream is read as multiple JSON objects separated by any number of whitespace characters between objects. Starting and trailing whitespace characters are also permitted.
+ * Each individual object is parsed lazily, when it is requested from the resulting sequence.
+ *
+ * Whitespace character is either ' ', '\n', '\r' or '\t'.
+ *
+ * Example of `WHITESPACE_SEPARATED` stream content:
+ * ```
+ * """{"key": "value"}{"key": "value2"} {"key2": "value2"}"""
+ * ```
+ */
+ WHITESPACE_SEPARATED,
+
+ /**
+ * Declares that objects in the input stream are wrapped in the JSON array.
+ * Each individual object in the array is parsed lazily when it is requested from the resulting sequence.
+ *
+ * The stream is read as multiple JSON objects wrapped into a JSON array.
+ * The stream must start with an array start character `[` and end with an array end character `]`,
+ * otherwise, [JsonDecodingException] is thrown.
+ *
+ * Example of `ARRAY_WRAPPED` stream content:
+ * ```
+ * """[{"key": "value"}, {"key": "value2"},{"key2": "value2"}]"""
+ * ```
+ */
+ ARRAY_WRAPPED,
+
+ /**
+ * Declares that parser itself should select between [WHITESPACE_SEPARATED] and [ARRAY_WRAPPED] modes.
+ * The selection is performed by looking on the first meaningful character of the stream.
+ *
+ * In most cases, auto-detection is sufficient to correctly parse an input.
+ * If the input is _whitespace-separated stream of the arrays_, parser could select an incorrect mode,
+ * for that [DecodeSequenceMode] must be specified explicitly.
+ *
+ * Example of an exceptional case:
+ * `[1, 2, 3] [4, 5, 6]\n[7, 8, 9]`
+ */
+ AUTO_DETECT;
+}
+
+/**
+ * Transforms the given [stream] into lazily deserialized sequence of elements of type [T] using UTF-8 encoding and [deserializer].
+ * Unlike [decodeFromStream], [stream] is allowed to have more than one element, separated as [format] declares.
+ *
+ * Elements must all be of type [T].
+ * Elements are parsed lazily when resulting [Sequence] is evaluated.
+ * Resulting sequence is tied to the stream and can be evaluated only once.
+ *
+ * **Resource caution:** this method neither closes the [stream] when the parsing is finished nor provides a method to close it manually.
+ * It is a caller responsibility to hold a reference to a stream and close it. Moreover, because stream is parsed lazily,
+ * closing it before returned sequence is evaluated completely will result in [IOException] from decoder.
+ *
+ * @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
+ * @throws [IOException] If an I/O error occurs and stream can't be read from.
+ */
+@ExperimentalSerializationApi
+public fun <T> Json.decodeToSequence(
+ stream: InputStream,
+ deserializer: DeserializationStrategy<T>,
+ format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
+): Sequence<T> {
+ val lexer = ReaderJsonLexer(stream)
+ val iter = JsonIterator(format, this, lexer, deserializer)
+ return Sequence { iter }.constrainOnce()
+}
+
+/**
+ * Transforms the given [stream] into lazily deserialized sequence of elements of type [T] using UTF-8 encoding and deserializer retrieved from the reified type parameter.
+ * Unlike [decodeFromStream], [stream] is allowed to have more than one element, separated as [format] declares.
+ *
+ * Elements must all be of type [T].
+ * Elements are parsed lazily when resulting [Sequence] is evaluated.
+ * Resulting sequence is tied to the stream and constrained to be evaluated only once.
+ *
+ * **Resource caution:** this method does not close [stream] when the parsing is finished neither provides method to close it manually.
+ * It is a caller responsibility to hold a reference to a stream and close it. Moreover, because stream is parsed lazily,
+ * closing it before returned sequence is evaluated fully would result in [IOException] from decoder.
+ *
+ * @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
+ * @throws [IOException] If an I/O error occurs and stream can't be read from.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Json.decodeToSequence(
+ stream: InputStream,
+ format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
+): Sequence<T> = decodeToSequence(stream, serializersModule.serializer(), format)
+
diff --git a/formats/json/jvmMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt
new file mode 100644
index 00000000..e51b3de3
--- /dev/null
+++ b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt
@@ -0,0 +1,32 @@
+package kotlinx.serialization.json.internal
+
+import java.util.concurrent.*
+
+internal object CharArrayPool {
+ private val arrays = ArrayDeque<CharArray>()
+ private var charsTotal = 0
+ /*
+ * Not really documented kill switch as a workaround for potential
+ * (unlikely) problems with memory consumptions.
+ */
+ private val MAX_CHARS_IN_POOL = runCatching {
+ System.getProperty("kotlinx.serialization.json.pool.size").toIntOrNull()
+ }.getOrNull() ?: 1024 * 1024 // 2 MB seems to be a reasonable constraint, (1M of chars)
+
+ fun take(): CharArray {
+ /*
+ * Initially the pool is empty, so an instance will be allocated
+ * and the pool will be populated in the 'release'
+ */
+ val candidate = synchronized(this) {
+ arrays.removeLastOrNull()?.also { charsTotal -= it.size }
+ }
+ return candidate ?: CharArray(128)
+ }
+
+ fun release(array: CharArray) = synchronized(this) {
+ if (charsTotal + array.size >= MAX_CHARS_IN_POOL) return@synchronized
+ charsTotal += array.size
+ arrays.addLast(array)
+ }
+}
diff --git a/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonIterator.kt b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonIterator.kt
new file mode 100644
index 00000000..79003082
--- /dev/null
+++ b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonIterator.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("FunctionName")
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.DeserializationStrategy
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.json.*
+
+internal fun <T> JsonIterator(
+ mode: DecodeSequenceMode,
+ json: Json,
+ lexer: ReaderJsonLexer,
+ deserializer: DeserializationStrategy<T>
+): Iterator<T> = when (lexer.determineFormat(mode)) {
+ DecodeSequenceMode.WHITESPACE_SEPARATED -> JsonIteratorWsSeparated(
+ json,
+ lexer,
+ deserializer
+ ) // Can be many WS-separated independent arrays
+ DecodeSequenceMode.ARRAY_WRAPPED -> JsonIteratorArrayWrapped(
+ json,
+ lexer,
+ deserializer
+ )
+ DecodeSequenceMode.AUTO_DETECT -> error("AbstractJsonLexer.determineFormat must be called beforehand.")
+}
+
+
+private fun AbstractJsonLexer.determineFormat(suggested: DecodeSequenceMode): DecodeSequenceMode = when (suggested) {
+ DecodeSequenceMode.WHITESPACE_SEPARATED ->
+ DecodeSequenceMode.WHITESPACE_SEPARATED // do not call consumeStartArray here so we don't confuse parser with stream of lists
+ DecodeSequenceMode.ARRAY_WRAPPED ->
+ if (tryConsumeStartArray()) DecodeSequenceMode.ARRAY_WRAPPED
+ else fail(TC_BEGIN_LIST)
+ DecodeSequenceMode.AUTO_DETECT ->
+ if (tryConsumeStartArray()) DecodeSequenceMode.ARRAY_WRAPPED
+ else DecodeSequenceMode.WHITESPACE_SEPARATED
+}
+
+private fun AbstractJsonLexer.tryConsumeStartArray(): Boolean {
+ if (peekNextToken() == TC_BEGIN_LIST) {
+ consumeNextToken(TC_BEGIN_LIST)
+ return true
+ }
+ return false
+}
+
+private class JsonIteratorWsSeparated<T>(
+ private val json: Json,
+ private val lexer: ReaderJsonLexer,
+ private val deserializer: DeserializationStrategy<T>
+) : Iterator<T> {
+ override fun next(): T =
+ StreamingJsonDecoder(json, WriteMode.OBJ, lexer, deserializer.descriptor)
+ .decodeSerializableValue(deserializer)
+
+ override fun hasNext(): Boolean = lexer.isNotEof()
+}
+
+private class JsonIteratorArrayWrapped<T>(
+ private val json: Json,
+ private val lexer: ReaderJsonLexer,
+ private val deserializer: DeserializationStrategy<T>
+) : Iterator<T> {
+ private var first = true
+
+ override fun next(): T {
+ if (first) {
+ first = false
+ } else {
+ lexer.consumeNextToken(COMMA)
+ }
+ val input = StreamingJsonDecoder(json, WriteMode.OBJ, lexer, deserializer.descriptor)
+ return input.decodeSerializableValue(deserializer)
+ }
+
+ /**
+ * Note: if array separator (comma) is missing, hasNext() returns true, but next() throws an exception.
+ */
+ override fun hasNext(): Boolean {
+ if (lexer.peekNextToken() == TC_END_LIST) {
+ lexer.consumeNextToken(TC_END_LIST)
+ if (lexer.isNotEof()) {
+ if (lexer.peekNextToken() == TC_BEGIN_LIST) lexer.fail("There is a start of the new array after the one parsed to sequence. " +
+ "${DecodeSequenceMode.ARRAY_WRAPPED.name} mode doesn't merge consecutive arrays.\n" +
+ "If you need to parse a stream of arrays, please use ${DecodeSequenceMode.WHITESPACE_SEPARATED.name} mode instead.")
+ lexer.expectEof()
+ }
+ return false
+ }
+ if (!lexer.isNotEof()) lexer.fail(TC_END_LIST)
+ return true
+ }
+}
diff --git a/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonLexerJvm.kt b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonLexerJvm.kt
new file mode 100644
index 00000000..eabfd088
--- /dev/null
+++ b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonLexerJvm.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import java.io.*
+import java.nio.charset.Charset
+
+internal const val BATCH_SIZE = 16 * 1024
+private const val DEFAULT_THRESHOLD = 128
+
+
+// This size of buffered reader is very important here, because utf-8 decoding is slow.
+// Jackson and Moshi are faster because they have specialized UTF-8 parser directly over InputStream
+internal const val READER_BUF_SIZE = 16 * BATCH_SIZE
+
+
+/**
+ * For some reason this hand-rolled implementation is faster than
+ * fun ArrayAsSequence(s: CharArray): CharSequence = java.nio.CharBuffer.wrap(s, 0, length)
+ */
+private class ArrayAsSequence(private val source: CharArray) : CharSequence {
+ override val length: Int = source.size
+
+ override fun get(index: Int): Char = source[index]
+
+ override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
+ return String(source, startIndex, endIndex - startIndex)
+ }
+}
+
+internal class ReaderJsonLexer(
+ private val reader: Reader,
+ private var _source: CharArray = CharArray(BATCH_SIZE)
+) : AbstractJsonLexer() {
+ private var threshold: Int = DEFAULT_THRESHOLD // chars
+
+ constructor(i: InputStream, charset: Charset = Charsets.UTF_8) : this(i.reader(charset).buffered(READER_BUF_SIZE))
+
+ override var source: CharSequence = ArrayAsSequence(_source)
+
+ init {
+ preload(0)
+ }
+
+ override fun tryConsumeComma(): Boolean {
+ val current = skipWhitespaces()
+ if (current >= source.length || current == -1) return false
+ if (source[current] == ',') {
+ ++currentPosition
+ return true
+ }
+ return false
+ }
+
+ override fun canConsumeValue(): Boolean {
+ ensureHaveChars()
+ var current = currentPosition
+ while (true) {
+ current = prefetchOrEof(current)
+ if (current == -1) break // could be inline function but KT-1436
+ val c = source[current]
+ // Inlined skipWhitespaces without field spill and nested loop. Also faster then char2TokenClass
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
+ ++current
+ continue
+ }
+ currentPosition = current
+ return isValidValueStart(c)
+ }
+ currentPosition = current
+ return false
+ }
+
+ private fun preload(spaceLeft: Int) {
+ val buffer = _source
+ System.arraycopy(buffer, currentPosition, buffer, 0, spaceLeft)
+ var read = spaceLeft
+ val sizeTotal = _source.size
+ while (read != sizeTotal) {
+ val actual = reader.read(buffer, read, sizeTotal - read)
+ if (actual == -1) {
+ // EOF, resizing the array so it matches input size
+ // Can also be done by extracting source.length to a separate var
+ _source = _source.copyOf(read)
+ source = ArrayAsSequence(_source)
+ threshold = -1
+ break
+ }
+ read += actual
+ }
+ currentPosition = 0
+ }
+
+ override fun prefetchOrEof(position: Int): Int {
+ if (position < source.length) return position
+ currentPosition = position
+ ensureHaveChars()
+ if (currentPosition != 0 || source.isEmpty()) return -1 // if something was loaded, then it would be zero.
+ return 0
+ }
+
+ override fun consumeNextToken(): Byte {
+ ensureHaveChars()
+ val source = source
+ var cpos = currentPosition
+ while (true) {
+ cpos = prefetchOrEof(cpos)
+ if (cpos == -1) break
+ val ch = source[cpos++]
+ return when (val tc = charToTokenClass(ch)) {
+ TC_WHITESPACE -> continue
+ else -> {
+ currentPosition = cpos
+ tc
+ }
+ }
+ }
+ currentPosition = cpos
+ return TC_EOF
+ }
+
+ override fun ensureHaveChars() {
+ val cur = currentPosition
+ val oldSize = _source.size
+ val spaceLeft = oldSize - cur
+ if (spaceLeft > threshold) return
+ // warning: current position is not updated during string consumption
+ // resizing
+ preload(spaceLeft)
+ }
+
+ override fun consumeKeyString(): String {
+ /*
+ * For strings we assume that escaped symbols are rather an exception, so firstly
+ * we optimistically scan for closing quote via intrinsified and blazing-fast 'indexOf',
+ * than do our pessimistic check for backslash and fallback to slow-path if necessary.
+ */
+ consumeNextToken(STRING)
+ var current = currentPosition
+ val closingQuote = indexOf('"', current)
+ if (closingQuote == -1) {
+ current = prefetchOrEof(current)
+ if (current == -1) fail(TC_STRING)
+ // it's also possible just to resize buffer,
+ // instead of falling back to slow path,
+ // not sure what is better
+ else return consumeString(source, currentPosition, current)
+ }
+ // Now we _optimistically_ know where the string ends (it might have been an escaped quote)
+ for (i in current until closingQuote) {
+ // Encountered escape sequence, should fallback to "slow" path and symmbolic scanning
+ if (source[i] == STRING_ESC) {
+ return consumeString(source, currentPosition, i)
+ }
+ }
+ this.currentPosition = closingQuote + 1
+ return substring(current, closingQuote)
+ }
+
+ override fun indexOf(char: Char, startPos: Int): Int {
+ val src = _source
+ for (i in startPos until src.size) {
+ if (src[i] == char) return i
+ }
+ return -1
+ }
+
+ override fun substring(startPos: Int, endPos: Int): String {
+ return String(_source, startPos, endPos - startPos)
+ }
+
+ override fun appendRange(fromIndex: Int, toIndex: Int) {
+ escapedString.append(_source, fromIndex, toIndex - fromIndex)
+ }
+}
diff --git a/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt
new file mode 100644
index 00000000..37766d99
--- /dev/null
+++ b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt
@@ -0,0 +1,136 @@
+package kotlinx.serialization.json.internal
+
+/**
+ * Optimized version of StringBuilder that is specific to JSON-encoding.
+ *
+ * ## Implementation note
+ *
+ * In order to encode a single string, it should be processed symbol-per-symbol,
+ * in order to detect and escape unicode symbols.
+ *
+ * Doing naively, it drastically slows down strings processing due to factors:
+ * * Byte-by-byte copying that does not leverage optimized array copying
+ * * A lot of range and flags checks due to Java's compact strings
+ *
+ * The following technique is used:
+ * 1) Instead of storing intermediate result in `StringBuilder`, we store it in
+ * `CharArray` directly, skipping compact strings checks in `StringBuilder`
+ * 2) Instead of copying symbols one-by-one, we optimistically copy it in batch using
+ * optimized and intrinsified `string.toCharArray(destination)`.
+ * It copies the content by up-to 8 times faster.
+ * Then we iterate over the char-array and execute single check over
+ * each character that is easily unrolled and vectorized by the inliner.
+ * If escape character is found, we fallback to per-symbol processing.
+ *
+ * 3) We pool char arrays in order to save excess resizes, allocations
+ * and nulls-out of arrays.
+ */
+internal actual open class JsonStringBuilder(@JvmField protected var array: CharArray) {
+ actual constructor(): this(CharArrayPool.take())
+
+ protected var size = 0
+
+ actual fun append(value: Long) {
+ // Can be hand-rolled, but requires a lot of code and corner-cases handling
+ append(value.toString())
+ }
+
+ actual fun append(ch: Char) {
+ ensureAdditionalCapacity(1)
+ array[size++] = ch
+ }
+
+ actual fun append(string: String) {
+ val length = string.length
+ ensureAdditionalCapacity(length)
+ string.toCharArray(array, size, 0, string.length)
+ size += length
+ }
+
+ actual fun appendQuoted(string: String) {
+ ensureAdditionalCapacity(string.length + 2)
+ val arr = array
+ var sz = size
+ arr[sz++] = '"'
+ val length = string.length
+ string.toCharArray(arr, sz, 0, length)
+ for (i in sz until sz + length) {
+ val ch = arr[i].code
+ // Do we have unescaped symbols?
+ if (ch < ESCAPE_MARKERS.size && ESCAPE_MARKERS[ch] != 0.toByte()) {
+ // Go to slow path
+ return appendStringSlowPath(i - sz, i, string)
+ }
+ }
+ // Update the state
+ // Capacity is not ensured because we didn't hit the slow path and thus guessed it properly in the beginning
+ sz += length
+ arr[sz++] = '"'
+ size = sz
+ }
+
+ private fun appendStringSlowPath(firstEscapedChar: Int, currentSize: Int, string: String) {
+ var sz = currentSize
+ for (i in firstEscapedChar until string.length) {
+ /*
+ * We ar already on slow path and haven't guessed the capacity properly.
+ * Reserve +2 for backslash-escaped symbols on each iteration
+ */
+ sz = ensureTotalCapacity(sz, 2)
+ val ch = string[i].code
+ // Do we have unescaped symbols?
+ if (ch < ESCAPE_MARKERS.size) {
+ /*
+ * Escape markers are populated for backslash-escaped symbols.
+ * E.g. ESCAPE_MARKERS['\b'] == 'b'.toByte()
+ * Everything else is populated with either zeros (no escapes)
+ * or ones (unicode escape)
+ */
+ when (val marker = ESCAPE_MARKERS[ch]) {
+ 0.toByte() -> {
+ array[sz++] = ch.toChar()
+ }
+ 1.toByte() -> {
+ val escapedString = ESCAPE_STRINGS[ch]!!
+ sz = ensureTotalCapacity(sz, escapedString.length)
+ escapedString.toCharArray(array, sz, 0, escapedString.length)
+ sz += escapedString.length
+ size = sz // Update size so the next resize will take it into account
+ }
+ else -> {
+ array[sz] = '\\'
+ array[sz + 1] = marker.toInt().toChar()
+ sz += 2
+ size = sz // Update size so the next resize will take it into account
+ }
+ }
+ } else {
+ array[sz++] = ch.toChar()
+ }
+ }
+ sz = ensureTotalCapacity(sz, 1)
+ array[sz++] = '"'
+ size = sz
+ }
+
+ actual override fun toString(): String {
+ return String(array, 0, size)
+ }
+
+ private fun ensureAdditionalCapacity(expected: Int) {
+ ensureTotalCapacity(size, expected)
+ }
+
+ // Old size is passed and returned separately to avoid excessive [size] field read
+ protected open fun ensureTotalCapacity(oldSize: Int, additional: Int): Int {
+ val newSize = oldSize + additional
+ if (array.size <= newSize) {
+ array = array.copyOf(newSize.coerceAtLeast(oldSize * 2))
+ }
+ return oldSize
+ }
+
+ actual open fun release() {
+ CharArrayPool.release(array)
+ }
+}
diff --git a/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonToWriterStringBuilder.kt b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonToWriterStringBuilder.kt
new file mode 100644
index 00000000..c4069068
--- /dev/null
+++ b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JsonToWriterStringBuilder.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import java.io.OutputStream
+import java.io.Writer
+import java.nio.charset.Charset
+
+
+internal class JsonToWriterStringBuilder(private val writer: Writer) : JsonStringBuilder(
+ // maybe this can also be taken from the pool, but currently initial char array size there is 128, which is too low.
+ CharArray(BATCH_SIZE)
+) {
+ constructor(os: OutputStream, charset: Charset = Charsets.UTF_8): this(os.writer(charset).buffered(READER_BUF_SIZE))
+
+ override fun ensureTotalCapacity(oldSize: Int, additional: Int): Int {
+ val requiredSize = oldSize + additional
+ val currentSize = array.size
+ if (currentSize <= requiredSize) {
+ flush(oldSize)
+ if (additional > currentSize) {
+ // Handle strings that are longer than buffer:
+ // Ideally, we should make `ensureAdditionalCapacity` return boolean and fall back
+ // to per-symbol path in appendQuoted on large strings,
+ // but this approach is adequate for current stage, too.
+ array = CharArray(requiredSize.coerceAtLeast(currentSize * 2))
+ }
+ return 0
+ }
+ return oldSize
+ }
+
+ private fun flush(sz: Int = size) {
+ writer.write(array, 0, sz)
+ size = 0
+ }
+
+ override fun release() {
+ flush()
+ writer.flush()
+ }
+}
+
diff --git a/formats/json/jvmMain/src/kotlinx/serialization/json/internal/createMapForCache.kt b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/createMapForCache.kt
new file mode 100644
index 00000000..cb249845
--- /dev/null
+++ b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/createMapForCache.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+import java.util.concurrent.*
+
+/**
+ * Creates a ConcurrentHashMap on JVM and regular HashMap on other platforms.
+ * To make actual use of cache in Kotlin/Native, mark a top-level object with this map
+ * as a @[ThreadLocal].
+ */
+internal actual fun <K, V> createMapForCache(initialCapacity: Int): MutableMap<K, V> =
+ ConcurrentHashMap(initialCapacity)
diff --git a/formats/json/jvmMainModule/src/module-info.java b/formats/json/jvmMainModule/src/module-info.java
new file mode 100644
index 00000000..c19220f0
--- /dev/null
+++ b/formats/json/jvmMainModule/src/module-info.java
@@ -0,0 +1,6 @@
+module kotlinx.serialization.json {
+ requires transitive kotlin.stdlib;
+ requires transitive kotlinx.serialization.core;
+
+ exports kotlinx.serialization.json;
+}
diff --git a/formats/json/jvmTest/resources/corner_cases/listing.txt b/formats/json/jvmTest/resources/corner_cases/listing.txt
new file mode 100644
index 00000000..caa82819
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/listing.txt
@@ -0,0 +1,18 @@
+number_1.0.json
+number_1.000000000000000005.json
+number_1000000000000000.json
+number_10000000000000000999.json
+number_1e-999.json
+number_1e6.json
+object_key_nfc_nfd.json
+object_key_nfd_nfc.json
+object_same_key_different_values.json
+object_same_key_same_value.json
+object_same_key_unclear_values.json
+string_1_escaped_invalid_codepoint.json
+string_1_invalid_codepoint.json
+string_2_escaped_invalid_codepoints.json
+string_2_invalid_codepoints.json
+string_3_escaped_invalid_codepoints.json
+string_3_invalid_codepoints.json
+string_with_escaped_NULL.json \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/number_1.0.json b/formats/json/jvmTest/resources/corner_cases/number_1.0.json
new file mode 100644
index 00000000..e7a19a6e
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/number_1.0.json
@@ -0,0 +1 @@
+[1.0] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/number_1.000000000000000005.json b/formats/json/jvmTest/resources/corner_cases/number_1.000000000000000005.json
new file mode 100644
index 00000000..c73b7cfc
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/number_1.000000000000000005.json
@@ -0,0 +1 @@
+[1.000000000000000005] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/number_1000000000000000.json b/formats/json/jvmTest/resources/corner_cases/number_1000000000000000.json
new file mode 100644
index 00000000..cd38afa7
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/number_1000000000000000.json
@@ -0,0 +1 @@
+[1000000000000000]
diff --git a/formats/json/jvmTest/resources/corner_cases/number_10000000000000000999.json b/formats/json/jvmTest/resources/corner_cases/number_10000000000000000999.json
new file mode 100644
index 00000000..946d13d3
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/number_10000000000000000999.json
@@ -0,0 +1 @@
+[10000000000000000999] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/number_1e-999.json b/formats/json/jvmTest/resources/corner_cases/number_1e-999.json
new file mode 100644
index 00000000..c8ed222f
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/number_1e-999.json
@@ -0,0 +1 @@
+[1E-999] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/number_1e6.json b/formats/json/jvmTest/resources/corner_cases/number_1e6.json
new file mode 100644
index 00000000..1a8b0f78
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/number_1e6.json
@@ -0,0 +1 @@
+[1E6] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/object_key_nfc_nfd.json b/formats/json/jvmTest/resources/corner_cases/object_key_nfc_nfd.json
new file mode 100644
index 00000000..e4cbc1dc
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/object_key_nfc_nfd.json
@@ -0,0 +1 @@
+{"é":"NFC","eÌ":"NFD"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/object_key_nfd_nfc.json b/formats/json/jvmTest/resources/corner_cases/object_key_nfd_nfc.json
new file mode 100644
index 00000000..b04ece18
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/object_key_nfd_nfc.json
@@ -0,0 +1 @@
+{"eÌ":"NFD","é":"NFC"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/object_same_key_different_values.json b/formats/json/jvmTest/resources/corner_cases/object_same_key_different_values.json
new file mode 100644
index 00000000..0c4547df
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/object_same_key_different_values.json
@@ -0,0 +1 @@
+{"a":1,"a":2} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/object_same_key_same_value.json b/formats/json/jvmTest/resources/corner_cases/object_same_key_same_value.json
new file mode 100644
index 00000000..e1070184
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/object_same_key_same_value.json
@@ -0,0 +1 @@
+{"a":1,"a":1} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/object_same_key_unclear_values.json b/formats/json/jvmTest/resources/corner_cases/object_same_key_unclear_values.json
new file mode 100644
index 00000000..8a76bd4f
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/object_same_key_unclear_values.json
@@ -0,0 +1 @@
+{"a":0, "a":-0}
diff --git a/formats/json/jvmTest/resources/corner_cases/string_1_escaped_invalid_codepoint.json b/formats/json/jvmTest/resources/corner_cases/string_1_escaped_invalid_codepoint.json
new file mode 100755
index 00000000..8e624731
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/string_1_escaped_invalid_codepoint.json
@@ -0,0 +1 @@
+["\uD800"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/string_1_invalid_codepoint.json b/formats/json/jvmTest/resources/corner_cases/string_1_invalid_codepoint.json
new file mode 100755
index 00000000..916bff92
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/string_1_invalid_codepoint.json
@@ -0,0 +1 @@
+["í €"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/string_2_escaped_invalid_codepoints.json b/formats/json/jvmTest/resources/corner_cases/string_2_escaped_invalid_codepoints.json
new file mode 100755
index 00000000..93568e2c
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/string_2_escaped_invalid_codepoints.json
@@ -0,0 +1 @@
+["\uD800\uD800"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/string_2_invalid_codepoints.json b/formats/json/jvmTest/resources/corner_cases/string_2_invalid_codepoints.json
new file mode 100755
index 00000000..043a72e8
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/string_2_invalid_codepoints.json
@@ -0,0 +1 @@
+["í €í €"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/string_3_escaped_invalid_codepoints.json b/formats/json/jvmTest/resources/corner_cases/string_3_escaped_invalid_codepoints.json
new file mode 100755
index 00000000..407dc657
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/string_3_escaped_invalid_codepoints.json
@@ -0,0 +1 @@
+["\uD800\uD800\uD800"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/string_3_invalid_codepoints.json b/formats/json/jvmTest/resources/corner_cases/string_3_invalid_codepoints.json
new file mode 100755
index 00000000..2fcb0927
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/string_3_invalid_codepoints.json
@@ -0,0 +1 @@
+["í €í €í €"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corner_cases/string_with_escaped_NULL.json b/formats/json/jvmTest/resources/corner_cases/string_with_escaped_NULL.json
new file mode 100644
index 00000000..8ca2be59
--- /dev/null
+++ b/formats/json/jvmTest/resources/corner_cases/string_with_escaped_NULL.json
@@ -0,0 +1 @@
+["A\u0000B"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/corpus.zip b/formats/json/jvmTest/resources/corpus.zip
new file mode 100644
index 00000000..a43f2952
--- /dev/null
+++ b/formats/json/jvmTest/resources/corpus.zip
Binary files differ
diff --git a/formats/json/jvmTest/resources/spec_cases/listing.txt b/formats/json/jvmTest/resources/spec_cases/listing.txt
new file mode 100644
index 00000000..c2f347cf
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/listing.txt
@@ -0,0 +1,231 @@
+//n_array_a_invalid_utf8.json
+//n_array_invalid_utf8.json
+//n_array_just_minus.json
+//n_array_star_inside.json
+//n_incomplete_false.json
+//n_incomplete_null.json
+//n_incomplete_true.json
+//n_object_bad_value.json
+//n_object_key_with_single_quotes.json
+//n_object_non_string_key.json
+//n_object_non_string_key_but_huge_number_instead.json
+//n_object_single_quote.json
+//n_object_unquoted_key.json
+//n_string_accentuated_char_no_quotes.json
+//n_string_single_string_no_double_quotes.json
+//n_string_unescaped_crtl_char.json
+//n_string_unescaped_newline.json
+//n_string_unescaped_tab.json
+//n_structure_U+2060_word_joined.json
+//n_structure_UTF8_BOM_no_data.json
+//n_structure_angle_bracket_..json
+//n_structure_angle_bracket_null.json
+//n_structure_ascii-unicode-identifier.json
+//n_structure_capitalized_True.json
+//n_structure_lone-invalid-utf-8.json
+//n_structure_lone-open-bracket.json
+//n_structure_number_with_trailing_garbage.json
+//n_structure_single_star.json
+//n_structure_unicode-identifier.json
+//n_structure_whitespace_U+2060_word_joiner.json
+n_array_1_true_without_comma.json
+n_array_colon_instead_of_comma.json
+n_array_comma_after_close.json
+n_array_comma_and_number.json
+n_array_double_comma.json
+n_array_double_extra_comma.json
+n_array_extra_close.json
+n_array_extra_comma.json
+n_array_incomplete.json
+n_array_incomplete_invalid_value.json
+n_array_inner_array_no_comma.json
+n_array_items_separated_by_semicolon.json
+n_array_just_comma.json
+n_array_missing_value.json
+n_array_newlines_unclosed.json
+n_array_number_and_comma.json
+n_array_number_and_several_commas.json
+n_array_spaces_vertical_tab_formfeed.json
+n_array_unclosed.json
+n_array_unclosed_trailing_comma.json
+n_array_unclosed_with_new_lines.json
+n_array_unclosed_with_object_inside.json
+n_multidigit_number_then_00.json
+n_object_bracket_key.json
+n_object_comma_instead_of_colon.json
+n_object_double_colon.json
+n_object_emoji.json
+n_object_garbage_at_end.json
+n_object_lone_continuation_byte_in_key_and_trailing_comma.json
+n_object_missing_colon.json
+n_object_missing_key.json
+n_object_missing_semicolon.json
+n_object_missing_value.json
+n_object_no-colon.json
+n_object_repeated_null_null.json
+n_object_several_trailing_commas.json
+n_object_trailing_comma.json
+n_object_trailing_comment.json
+n_object_trailing_comment_open.json
+n_object_trailing_comment_slash_open.json
+n_object_trailing_comment_slash_open_incomplete.json
+n_object_two_commas_in_a_row.json
+n_object_unterminated-value.json
+n_object_with_single_string.json
+n_object_with_trailing_garbage.json
+n_single_space.json
+n_string_1_surrogate_then_escape.json
+n_string_1_surrogate_then_escape_u.json
+n_string_1_surrogate_then_escape_u1.json
+n_string_1_surrogate_then_escape_u1x.json
+n_string_backslash_00.json
+n_string_escape_x.json
+n_string_escaped_backslash_bad.json
+n_string_escaped_ctrl_char_tab.json
+n_string_escaped_emoji.json
+n_string_incomplete_escape.json
+n_string_incomplete_escaped_character.json
+n_string_incomplete_surrogate.json
+n_string_incomplete_surrogate_escape_invalid.json
+n_string_invalid-utf-8-in-escape.json
+n_string_invalid_backslash_esc.json
+n_string_invalid_unicode_escape.json
+n_string_invalid_utf8_after_escape.json
+n_string_leading_uescaped_thinspace.json
+n_string_no_quotes_with_bad_escape.json
+n_string_single_doublequote.json
+n_string_single_quote.json
+n_string_start_escape_unclosed.json
+n_string_unicode_CapitalU.json
+n_string_with_trailing_garbage.json
+n_structure_100000_opening_arrays.json
+n_structure_array_trailing_garbage.json
+n_structure_array_with_extra_array_close.json
+n_structure_array_with_unclosed_string.json
+n_structure_close_unopened_array.json
+n_structure_comma_instead_of_closing_brace.json
+n_structure_double_array.json
+n_structure_end_array.json
+n_structure_incomplete_UTF8_BOM.json
+n_structure_no_data.json
+n_structure_null-byte-outside-string.json
+n_structure_object_followed_by_closing_object.json
+n_structure_object_unclosed_no_value.json
+n_structure_object_with_comment.json
+n_structure_object_with_trailing_garbage.json
+n_structure_open_array_apostrophe.json
+n_structure_open_array_comma.json
+n_structure_open_array_object.json
+n_structure_open_array_open_object.json
+n_structure_open_array_open_string.json
+n_structure_open_array_string.json
+n_structure_open_object.json
+n_structure_open_object_close_array.json
+n_structure_open_object_comma.json
+n_structure_open_object_open_array.json
+n_structure_open_object_open_string.json
+n_structure_open_object_string_with_apostrophes.json
+n_structure_open_open.json
+n_structure_trailing_#.json
+n_structure_uescaped_LF_before_string.json
+n_structure_unclosed_array.json
+n_structure_unclosed_array_partial_null.json
+n_structure_unclosed_array_unfinished_false.json
+n_structure_unclosed_array_unfinished_true.json
+n_structure_unclosed_object.json
+n_structure_whitespace_formfeed.json
+y_array_arraysWithSpaces.json
+y_array_empty-string.json
+y_array_empty.json
+y_array_ending_with_newline.json
+y_array_false.json
+y_array_heterogeneous.json
+y_array_null.json
+y_array_with_1_and_newline.json
+y_array_with_leading_space.json
+y_array_with_several_null.json
+y_array_with_trailing_space.json
+y_number.json
+y_number_0e+1.json
+y_number_0e1.json
+y_number_after_space.json
+y_number_double_close_to_zero.json
+y_number_int_with_exp.json
+y_number_minus_zero.json
+y_number_negative_int.json
+y_number_negative_one.json
+y_number_negative_zero.json
+y_number_real_capital_e.json
+y_number_real_capital_e_neg_exp.json
+y_number_real_capital_e_pos_exp.json
+y_number_real_exponent.json
+y_number_real_fraction_exponent.json
+y_number_real_neg_exp.json
+y_number_real_pos_exponent.json
+y_number_simple_int.json
+y_number_simple_real.json
+y_object.json
+y_object_basic.json
+y_object_duplicated_key.json
+y_object_duplicated_key_and_value.json
+y_object_empty.json
+y_object_empty_key.json
+y_object_escaped_null_in_key.json
+y_object_extreme_numbers.json
+y_object_long_strings.json
+y_object_simple.json
+y_object_string_unicode.json
+y_object_with_newlines.json
+y_string_1_2_3_bytes_UTF-8_sequences.json
+y_string_accepted_surrogate_pair.json
+y_string_accepted_surrogate_pairs.json
+y_string_allowed_escapes.json
+y_string_backslash_and_u_escaped_zero.json
+y_string_backslash_doublequotes.json
+y_string_comments.json
+y_string_double_escape_a.json
+y_string_double_escape_n.json
+y_string_escaped_control_character.json
+y_string_escaped_noncharacter.json
+y_string_in_array.json
+y_string_in_array_with_leading_space.json
+y_string_last_surrogates_1_and_2.json
+y_string_nbsp_uescaped.json
+y_string_nonCharacterInUTF-8_U+10FFFF.json
+y_string_nonCharacterInUTF-8_U+FFFF.json
+y_string_null_escape.json
+y_string_one-byte-utf-8.json
+y_string_pi.json
+y_string_reservedCharacterInUTF-8_U+1BFFF.json
+y_string_simple_ascii.json
+y_string_space.json
+y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json
+y_string_three-byte-utf-8.json
+y_string_two-byte-utf-8.json
+y_string_u+2028_line_sep.json
+y_string_u+2029_par_sep.json
+y_string_uEscape.json
+y_string_uescaped_newline.json
+y_string_unescaped_char_delete.json
+y_string_unicode.json
+y_string_unicodeEscapedBackslash.json
+y_string_unicode_2.json
+y_string_unicode_U+10FFFE_nonchar.json
+y_string_unicode_U+1FFFE_nonchar.json
+y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json
+y_string_unicode_U+2064_invisible_plus.json
+y_string_unicode_U+FDD0_nonchar.json
+y_string_unicode_U+FFFE_nonchar.json
+y_string_unicode_escaped_double_quote.json
+y_string_utf8.json
+y_string_with_del_character.json
+y_structure_lonely_false.json
+y_structure_lonely_int.json
+y_structure_lonely_negative_real.json
+y_structure_lonely_null.json
+y_structure_lonely_string.json
+y_structure_lonely_true.json
+y_structure_string_empty.json
+y_structure_trailing_newline.json
+y_structure_true_in_array.json
+y_structure_whitespace_array.json \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_1_true_without_comma.json b/formats/json/jvmTest/resources/spec_cases/n_array_1_true_without_comma.json
new file mode 100644
index 00000000..c14e3f6b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_1_true_without_comma.json
@@ -0,0 +1 @@
+[1 true] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_a_invalid_utf8.json b/formats/json/jvmTest/resources/spec_cases/n_array_a_invalid_utf8.json
new file mode 100644
index 00000000..38a86e2e
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_a_invalid_utf8.json
@@ -0,0 +1 @@
+[aå] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_colon_instead_of_comma.json b/formats/json/jvmTest/resources/spec_cases/n_array_colon_instead_of_comma.json
new file mode 100644
index 00000000..0d02ad44
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_colon_instead_of_comma.json
@@ -0,0 +1 @@
+["": 1] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_comma_after_close.json b/formats/json/jvmTest/resources/spec_cases/n_array_comma_after_close.json
new file mode 100644
index 00000000..2ccba8d9
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_comma_after_close.json
@@ -0,0 +1 @@
+[""], \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_comma_and_number.json b/formats/json/jvmTest/resources/spec_cases/n_array_comma_and_number.json
new file mode 100755
index 00000000..d2c84e37
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_comma_and_number.json
@@ -0,0 +1 @@
+[,1] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_double_comma.json b/formats/json/jvmTest/resources/spec_cases/n_array_double_comma.json
new file mode 100755
index 00000000..0431712b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_double_comma.json
@@ -0,0 +1 @@
+[1,,2] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_double_extra_comma.json b/formats/json/jvmTest/resources/spec_cases/n_array_double_extra_comma.json
new file mode 100644
index 00000000..3f01d312
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_double_extra_comma.json
@@ -0,0 +1 @@
+["x",,] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_extra_close.json b/formats/json/jvmTest/resources/spec_cases/n_array_extra_close.json
new file mode 100644
index 00000000..c12f9fae
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_extra_close.json
@@ -0,0 +1 @@
+["x"]] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_extra_comma.json b/formats/json/jvmTest/resources/spec_cases/n_array_extra_comma.json
new file mode 100644
index 00000000..5f8ce18e
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_extra_comma.json
@@ -0,0 +1 @@
+["",] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_incomplete.json b/formats/json/jvmTest/resources/spec_cases/n_array_incomplete.json
new file mode 100644
index 00000000..cc65b0b5
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_incomplete.json
@@ -0,0 +1 @@
+["x" \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_incomplete_invalid_value.json b/formats/json/jvmTest/resources/spec_cases/n_array_incomplete_invalid_value.json
new file mode 100644
index 00000000..c21a8f6c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_incomplete_invalid_value.json
@@ -0,0 +1 @@
+[x \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_inner_array_no_comma.json b/formats/json/jvmTest/resources/spec_cases/n_array_inner_array_no_comma.json
new file mode 100644
index 00000000..c70b7164
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_inner_array_no_comma.json
@@ -0,0 +1 @@
+[3[4]] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_invalid_utf8.json b/formats/json/jvmTest/resources/spec_cases/n_array_invalid_utf8.json
new file mode 100644
index 00000000..6099d344
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_invalid_utf8.json
@@ -0,0 +1 @@
+[ÿ] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_items_separated_by_semicolon.json b/formats/json/jvmTest/resources/spec_cases/n_array_items_separated_by_semicolon.json
new file mode 100755
index 00000000..d4bd7314
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_items_separated_by_semicolon.json
@@ -0,0 +1 @@
+[1:2] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_just_comma.json b/formats/json/jvmTest/resources/spec_cases/n_array_just_comma.json
new file mode 100755
index 00000000..9d7077c6
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_just_comma.json
@@ -0,0 +1 @@
+[,] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_just_minus.json b/formats/json/jvmTest/resources/spec_cases/n_array_just_minus.json
new file mode 100755
index 00000000..29501c6c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_just_minus.json
@@ -0,0 +1 @@
+[-] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_missing_value.json b/formats/json/jvmTest/resources/spec_cases/n_array_missing_value.json
new file mode 100644
index 00000000..3a6ba86f
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_missing_value.json
@@ -0,0 +1 @@
+[ , ""] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_newlines_unclosed.json b/formats/json/jvmTest/resources/spec_cases/n_array_newlines_unclosed.json
new file mode 100644
index 00000000..64668006
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_newlines_unclosed.json
@@ -0,0 +1,3 @@
+["a",
+4
+,1, \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_number_and_comma.json b/formats/json/jvmTest/resources/spec_cases/n_array_number_and_comma.json
new file mode 100755
index 00000000..13f6f1d1
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_number_and_comma.json
@@ -0,0 +1 @@
+[1,] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_number_and_several_commas.json b/formats/json/jvmTest/resources/spec_cases/n_array_number_and_several_commas.json
new file mode 100755
index 00000000..0ac408cb
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_number_and_several_commas.json
@@ -0,0 +1 @@
+[1,,] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_spaces_vertical_tab_formfeed.json b/formats/json/jvmTest/resources/spec_cases/n_array_spaces_vertical_tab_formfeed.json
new file mode 100755
index 00000000..6cd7cf58
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_spaces_vertical_tab_formfeed.json
@@ -0,0 +1 @@
+[" a"\f] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_star_inside.json b/formats/json/jvmTest/resources/spec_cases/n_array_star_inside.json
new file mode 100755
index 00000000..5a519464
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_star_inside.json
@@ -0,0 +1 @@
+[*] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_unclosed.json b/formats/json/jvmTest/resources/spec_cases/n_array_unclosed.json
new file mode 100644
index 00000000..06073305
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_unclosed.json
@@ -0,0 +1 @@
+["" \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_unclosed_trailing_comma.json b/formats/json/jvmTest/resources/spec_cases/n_array_unclosed_trailing_comma.json
new file mode 100644
index 00000000..6604698f
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_unclosed_trailing_comma.json
@@ -0,0 +1 @@
+[1, \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_unclosed_with_new_lines.json b/formats/json/jvmTest/resources/spec_cases/n_array_unclosed_with_new_lines.json
new file mode 100644
index 00000000..4f61de3f
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_unclosed_with_new_lines.json
@@ -0,0 +1,3 @@
+[1,
+1
+,1 \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_array_unclosed_with_object_inside.json b/formats/json/jvmTest/resources/spec_cases/n_array_unclosed_with_object_inside.json
new file mode 100644
index 00000000..043a87e2
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_array_unclosed_with_object_inside.json
@@ -0,0 +1 @@
+[{} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_incomplete_false.json b/formats/json/jvmTest/resources/spec_cases/n_incomplete_false.json
new file mode 100644
index 00000000..eb18c6a1
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_incomplete_false.json
@@ -0,0 +1 @@
+[fals] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_incomplete_null.json b/formats/json/jvmTest/resources/spec_cases/n_incomplete_null.json
new file mode 100644
index 00000000..c18ef538
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_incomplete_null.json
@@ -0,0 +1 @@
+[nul] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_incomplete_true.json b/formats/json/jvmTest/resources/spec_cases/n_incomplete_true.json
new file mode 100644
index 00000000..f451ac6d
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_incomplete_true.json
@@ -0,0 +1 @@
+[tru] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_multidigit_number_then_00.json b/formats/json/jvmTest/resources/spec_cases/n_multidigit_number_then_00.json
new file mode 100644
index 00000000..c22507b8
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_multidigit_number_then_00.json
Binary files differ
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_bad_value.json b/formats/json/jvmTest/resources/spec_cases/n_object_bad_value.json
new file mode 100644
index 00000000..a03a8c03
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_bad_value.json
@@ -0,0 +1 @@
+["x", truth] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_bracket_key.json b/formats/json/jvmTest/resources/spec_cases/n_object_bracket_key.json
new file mode 100644
index 00000000..cc443b48
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_bracket_key.json
@@ -0,0 +1 @@
+{[: "x"}
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_comma_instead_of_colon.json b/formats/json/jvmTest/resources/spec_cases/n_object_comma_instead_of_colon.json
new file mode 100644
index 00000000..8d563770
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_comma_instead_of_colon.json
@@ -0,0 +1 @@
+{"x", null} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_double_colon.json b/formats/json/jvmTest/resources/spec_cases/n_object_double_colon.json
new file mode 100644
index 00000000..80e8c7b8
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_double_colon.json
@@ -0,0 +1 @@
+{"x"::"b"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_emoji.json b/formats/json/jvmTest/resources/spec_cases/n_object_emoji.json
new file mode 100644
index 00000000..cb4078ea
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_emoji.json
@@ -0,0 +1 @@
+{🇨🇭} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_garbage_at_end.json b/formats/json/jvmTest/resources/spec_cases/n_object_garbage_at_end.json
new file mode 100644
index 00000000..80c42cba
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_garbage_at_end.json
@@ -0,0 +1 @@
+{"a":"a" 123} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_key_with_single_quotes.json b/formats/json/jvmTest/resources/spec_cases/n_object_key_with_single_quotes.json
new file mode 100755
index 00000000..77c32759
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_key_with_single_quotes.json
@@ -0,0 +1 @@
+{key: 'value'} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_lone_continuation_byte_in_key_and_trailing_comma.json b/formats/json/jvmTest/resources/spec_cases/n_object_lone_continuation_byte_in_key_and_trailing_comma.json
new file mode 100644
index 00000000..aa2cb637
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_lone_continuation_byte_in_key_and_trailing_comma.json
@@ -0,0 +1 @@
+{"¹":"0",} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_missing_colon.json b/formats/json/jvmTest/resources/spec_cases/n_object_missing_colon.json
new file mode 100644
index 00000000..b98eff62
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_missing_colon.json
@@ -0,0 +1 @@
+{"a" b} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_missing_key.json b/formats/json/jvmTest/resources/spec_cases/n_object_missing_key.json
new file mode 100755
index 00000000..b4fb0f52
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_missing_key.json
@@ -0,0 +1 @@
+{:"b"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_missing_semicolon.json b/formats/json/jvmTest/resources/spec_cases/n_object_missing_semicolon.json
new file mode 100755
index 00000000..e3451384
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_missing_semicolon.json
@@ -0,0 +1 @@
+{"a" "b"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_missing_value.json b/formats/json/jvmTest/resources/spec_cases/n_object_missing_value.json
new file mode 100644
index 00000000..3ef538a6
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_missing_value.json
@@ -0,0 +1 @@
+{"a": \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_no-colon.json b/formats/json/jvmTest/resources/spec_cases/n_object_no-colon.json
new file mode 100644
index 00000000..f3797b35
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_no-colon.json
@@ -0,0 +1 @@
+{"a" \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_non_string_key.json b/formats/json/jvmTest/resources/spec_cases/n_object_non_string_key.json
new file mode 100755
index 00000000..b9945b34
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_non_string_key.json
@@ -0,0 +1 @@
+{1:1} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_non_string_key_but_huge_number_instead.json b/formats/json/jvmTest/resources/spec_cases/n_object_non_string_key_but_huge_number_instead.json
new file mode 100755
index 00000000..b37fa86c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_non_string_key_but_huge_number_instead.json
@@ -0,0 +1 @@
+{9999E9999:1} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_repeated_null_null.json b/formats/json/jvmTest/resources/spec_cases/n_object_repeated_null_null.json
new file mode 100755
index 00000000..f7d2959d
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_repeated_null_null.json
@@ -0,0 +1 @@
+{null:null,null:null} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_several_trailing_commas.json b/formats/json/jvmTest/resources/spec_cases/n_object_several_trailing_commas.json
new file mode 100755
index 00000000..3c9afe8d
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_several_trailing_commas.json
@@ -0,0 +1 @@
+{"id":0,,,,,} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_single_quote.json b/formats/json/jvmTest/resources/spec_cases/n_object_single_quote.json
new file mode 100644
index 00000000..e5cdf976
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_single_quote.json
@@ -0,0 +1 @@
+{'a':0} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comma.json b/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comma.json
new file mode 100755
index 00000000..a4b02509
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comma.json
@@ -0,0 +1 @@
+{"id":0,} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment.json b/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment.json
new file mode 100644
index 00000000..a372c655
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment.json
@@ -0,0 +1 @@
+{"a":"b"}/**/ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_open.json b/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_open.json
new file mode 100644
index 00000000..d557f41c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_open.json
@@ -0,0 +1 @@
+{"a":"b"}/**// \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open.json b/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open.json
new file mode 100644
index 00000000..e335136c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open.json
@@ -0,0 +1 @@
+{"a":"b"}// \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open_incomplete.json b/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open_incomplete.json
new file mode 100644
index 00000000..d892e49f
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open_incomplete.json
@@ -0,0 +1 @@
+{"a":"b"}/ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_two_commas_in_a_row.json b/formats/json/jvmTest/resources/spec_cases/n_object_two_commas_in_a_row.json
new file mode 100755
index 00000000..7c639ae6
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_two_commas_in_a_row.json
@@ -0,0 +1 @@
+{"a":"b",,"c":"d"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_unquoted_key.json b/formats/json/jvmTest/resources/spec_cases/n_object_unquoted_key.json
new file mode 100644
index 00000000..8ba13729
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_unquoted_key.json
@@ -0,0 +1 @@
+{a: "b"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_unterminated-value.json b/formats/json/jvmTest/resources/spec_cases/n_object_unterminated-value.json
new file mode 100644
index 00000000..7fe699a6
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_unterminated-value.json
@@ -0,0 +1 @@
+{"a":"a \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_with_single_string.json b/formats/json/jvmTest/resources/spec_cases/n_object_with_single_string.json
new file mode 100644
index 00000000..d63f7fbb
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_with_single_string.json
@@ -0,0 +1 @@
+{ "foo" : "bar", "a" } \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_object_with_trailing_garbage.json b/formats/json/jvmTest/resources/spec_cases/n_object_with_trailing_garbage.json
new file mode 100644
index 00000000..787c8f0a
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_object_with_trailing_garbage.json
@@ -0,0 +1 @@
+{"a":"b"}# \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_single_space.json b/formats/json/jvmTest/resources/spec_cases/n_single_space.json
new file mode 100755
index 00000000..0519ecba
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_single_space.json
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape.json b/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape.json
new file mode 100644
index 00000000..acec66d8
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape.json
@@ -0,0 +1 @@
+["\uD800\"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u.json b/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u.json
new file mode 100644
index 00000000..e834b05e
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u.json
@@ -0,0 +1 @@
+["\uD800\u"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1.json b/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1.json
new file mode 100644
index 00000000..a04cd348
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1.json
@@ -0,0 +1 @@
+["\uD800\u1"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1x.json b/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1x.json
new file mode 100644
index 00000000..bfbd2340
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1x.json
@@ -0,0 +1 @@
+["\uD800\u1x"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_accentuated_char_no_quotes.json b/formats/json/jvmTest/resources/spec_cases/n_string_accentuated_char_no_quotes.json
new file mode 100644
index 00000000..fd689569
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_accentuated_char_no_quotes.json
@@ -0,0 +1 @@
+[é] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_backslash_00.json b/formats/json/jvmTest/resources/spec_cases/n_string_backslash_00.json
new file mode 100644
index 00000000..b5bf267b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_backslash_00.json
Binary files differ
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_escape_x.json b/formats/json/jvmTest/resources/spec_cases/n_string_escape_x.json
new file mode 100644
index 00000000..fae29193
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_escape_x.json
@@ -0,0 +1 @@
+["\x00"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_escaped_backslash_bad.json b/formats/json/jvmTest/resources/spec_cases/n_string_escaped_backslash_bad.json
new file mode 100755
index 00000000..016fcb47
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_escaped_backslash_bad.json
@@ -0,0 +1 @@
+["\\\"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_escaped_ctrl_char_tab.json b/formats/json/jvmTest/resources/spec_cases/n_string_escaped_ctrl_char_tab.json
new file mode 100644
index 00000000..f35ea382
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_escaped_ctrl_char_tab.json
@@ -0,0 +1 @@
+["\ "] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_escaped_emoji.json b/formats/json/jvmTest/resources/spec_cases/n_string_escaped_emoji.json
new file mode 100644
index 00000000..a2777542
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_escaped_emoji.json
@@ -0,0 +1 @@
+["\🌀"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_escape.json b/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_escape.json
new file mode 100755
index 00000000..3415c33c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_escape.json
@@ -0,0 +1 @@
+["\"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_escaped_character.json b/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_escaped_character.json
new file mode 100755
index 00000000..0f2197ea
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_escaped_character.json
@@ -0,0 +1 @@
+["\u00A"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_surrogate.json b/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_surrogate.json
new file mode 100755
index 00000000..75504a65
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_surrogate.json
@@ -0,0 +1 @@
+["\uD834\uDd"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_surrogate_escape_invalid.json b/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_surrogate_escape_invalid.json
new file mode 100755
index 00000000..bd965606
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_incomplete_surrogate_escape_invalid.json
@@ -0,0 +1 @@
+["\uD800\uD800\x"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_invalid-utf-8-in-escape.json b/formats/json/jvmTest/resources/spec_cases/n_string_invalid-utf-8-in-escape.json
new file mode 100644
index 00000000..0c430064
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_invalid-utf-8-in-escape.json
@@ -0,0 +1 @@
+["\uå"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_invalid_backslash_esc.json b/formats/json/jvmTest/resources/spec_cases/n_string_invalid_backslash_esc.json
new file mode 100755
index 00000000..d1eb6092
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_invalid_backslash_esc.json
@@ -0,0 +1 @@
+["\a"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_invalid_unicode_escape.json b/formats/json/jvmTest/resources/spec_cases/n_string_invalid_unicode_escape.json
new file mode 100644
index 00000000..7608cb6b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_invalid_unicode_escape.json
@@ -0,0 +1 @@
+["\uqqqq"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_invalid_utf8_after_escape.json b/formats/json/jvmTest/resources/spec_cases/n_string_invalid_utf8_after_escape.json
new file mode 100644
index 00000000..2f757a25
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_invalid_utf8_after_escape.json
@@ -0,0 +1 @@
+["\å"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_leading_uescaped_thinspace.json b/formats/json/jvmTest/resources/spec_cases/n_string_leading_uescaped_thinspace.json
new file mode 100755
index 00000000..7b297c63
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_leading_uescaped_thinspace.json
@@ -0,0 +1 @@
+[\u0020"asd"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_no_quotes_with_bad_escape.json b/formats/json/jvmTest/resources/spec_cases/n_string_no_quotes_with_bad_escape.json
new file mode 100644
index 00000000..01bc70ab
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_no_quotes_with_bad_escape.json
@@ -0,0 +1 @@
+[\n] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_single_doublequote.json b/formats/json/jvmTest/resources/spec_cases/n_string_single_doublequote.json
new file mode 100755
index 00000000..9d68933c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_single_doublequote.json
@@ -0,0 +1 @@
+" \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_single_quote.json b/formats/json/jvmTest/resources/spec_cases/n_string_single_quote.json
new file mode 100644
index 00000000..caff239b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_single_quote.json
@@ -0,0 +1 @@
+['single quote'] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_single_string_no_double_quotes.json b/formats/json/jvmTest/resources/spec_cases/n_string_single_string_no_double_quotes.json
new file mode 100755
index 00000000..f2ba8f84
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_single_string_no_double_quotes.json
@@ -0,0 +1 @@
+abc \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_start_escape_unclosed.json b/formats/json/jvmTest/resources/spec_cases/n_string_start_escape_unclosed.json
new file mode 100644
index 00000000..db62a46f
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_start_escape_unclosed.json
@@ -0,0 +1 @@
+["\ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_unescaped_crtl_char.json b/formats/json/jvmTest/resources/spec_cases/n_string_unescaped_crtl_char.json
new file mode 100755
index 00000000..9f213480
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_unescaped_crtl_char.json
Binary files differ
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_unescaped_newline.json b/formats/json/jvmTest/resources/spec_cases/n_string_unescaped_newline.json
new file mode 100644
index 00000000..700d3608
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_unescaped_newline.json
@@ -0,0 +1,2 @@
+["new
+line"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_unescaped_tab.json b/formats/json/jvmTest/resources/spec_cases/n_string_unescaped_tab.json
new file mode 100644
index 00000000..160264a2
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_unescaped_tab.json
@@ -0,0 +1 @@
+[" "] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_unicode_CapitalU.json b/formats/json/jvmTest/resources/spec_cases/n_string_unicode_CapitalU.json
new file mode 100644
index 00000000..17332bb1
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_unicode_CapitalU.json
@@ -0,0 +1 @@
+"\UA66D" \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_string_with_trailing_garbage.json b/formats/json/jvmTest/resources/spec_cases/n_string_with_trailing_garbage.json
new file mode 100644
index 00000000..efe3bd27
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_string_with_trailing_garbage.json
@@ -0,0 +1 @@
+""x \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_100000_opening_arrays.json b/formats/json/jvmTest/resources/spec_cases/n_structure_100000_opening_arrays.json
new file mode 100644
index 00000000..a4823eec
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_100000_opening_arrays.json
@@ -0,0 +1 @@
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_U+2060_word_joined.json b/formats/json/jvmTest/resources/spec_cases/n_structure_U+2060_word_joined.json
new file mode 100644
index 00000000..81156a69
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_U+2060_word_joined.json
@@ -0,0 +1 @@
+[â ] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_UTF8_BOM_no_data.json b/formats/json/jvmTest/resources/spec_cases/n_structure_UTF8_BOM_no_data.json
new file mode 100755
index 00000000..5f282702
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_UTF8_BOM_no_data.json
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_angle_bracket_..json b/formats/json/jvmTest/resources/spec_cases/n_structure_angle_bracket_..json
new file mode 100755
index 00000000..a56fef0b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_angle_bracket_..json
@@ -0,0 +1 @@
+<.> \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_angle_bracket_null.json b/formats/json/jvmTest/resources/spec_cases/n_structure_angle_bracket_null.json
new file mode 100755
index 00000000..617f2625
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_angle_bracket_null.json
@@ -0,0 +1 @@
+[<null>] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_array_trailing_garbage.json b/formats/json/jvmTest/resources/spec_cases/n_structure_array_trailing_garbage.json
new file mode 100644
index 00000000..5a745e6f
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_array_trailing_garbage.json
@@ -0,0 +1 @@
+[1]x \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_array_with_extra_array_close.json b/formats/json/jvmTest/resources/spec_cases/n_structure_array_with_extra_array_close.json
new file mode 100755
index 00000000..6cfb1398
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_array_with_extra_array_close.json
@@ -0,0 +1 @@
+[1]] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_array_with_unclosed_string.json b/formats/json/jvmTest/resources/spec_cases/n_structure_array_with_unclosed_string.json
new file mode 100755
index 00000000..ba6b1788
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_array_with_unclosed_string.json
@@ -0,0 +1 @@
+["asd] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_ascii-unicode-identifier.json b/formats/json/jvmTest/resources/spec_cases/n_structure_ascii-unicode-identifier.json
new file mode 100644
index 00000000..ef2ab62f
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_ascii-unicode-identifier.json
@@ -0,0 +1 @@
+aå \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_capitalized_True.json b/formats/json/jvmTest/resources/spec_cases/n_structure_capitalized_True.json
new file mode 100755
index 00000000..7cd88469
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_capitalized_True.json
@@ -0,0 +1 @@
+[True] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_close_unopened_array.json b/formats/json/jvmTest/resources/spec_cases/n_structure_close_unopened_array.json
new file mode 100755
index 00000000..d2af0c64
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_close_unopened_array.json
@@ -0,0 +1 @@
+1] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_comma_instead_of_closing_brace.json b/formats/json/jvmTest/resources/spec_cases/n_structure_comma_instead_of_closing_brace.json
new file mode 100644
index 00000000..ac61b820
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_comma_instead_of_closing_brace.json
@@ -0,0 +1 @@
+{"x": true, \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_double_array.json b/formats/json/jvmTest/resources/spec_cases/n_structure_double_array.json
new file mode 100755
index 00000000..058d1626
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_double_array.json
@@ -0,0 +1 @@
+[][] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_end_array.json b/formats/json/jvmTest/resources/spec_cases/n_structure_end_array.json
new file mode 100644
index 00000000..54caf60b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_end_array.json
@@ -0,0 +1 @@
+] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_incomplete_UTF8_BOM.json b/formats/json/jvmTest/resources/spec_cases/n_structure_incomplete_UTF8_BOM.json
new file mode 100755
index 00000000..bfcdd514
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_incomplete_UTF8_BOM.json
@@ -0,0 +1 @@
+ï»{} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_lone-invalid-utf-8.json b/formats/json/jvmTest/resources/spec_cases/n_structure_lone-invalid-utf-8.json
new file mode 100644
index 00000000..8b1296ca
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_lone-invalid-utf-8.json
@@ -0,0 +1 @@
+å \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_lone-open-bracket.json b/formats/json/jvmTest/resources/spec_cases/n_structure_lone-open-bracket.json
new file mode 100644
index 00000000..8e2f0bef
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_lone-open-bracket.json
@@ -0,0 +1 @@
+[ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_no_data.json b/formats/json/jvmTest/resources/spec_cases/n_structure_no_data.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_no_data.json
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_null-byte-outside-string.json b/formats/json/jvmTest/resources/spec_cases/n_structure_null-byte-outside-string.json
new file mode 100644
index 00000000..326db144
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_null-byte-outside-string.json
Binary files differ
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_number_with_trailing_garbage.json b/formats/json/jvmTest/resources/spec_cases/n_structure_number_with_trailing_garbage.json
new file mode 100644
index 00000000..0746539d
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_number_with_trailing_garbage.json
@@ -0,0 +1 @@
+2@ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_object_followed_by_closing_object.json b/formats/json/jvmTest/resources/spec_cases/n_structure_object_followed_by_closing_object.json
new file mode 100644
index 00000000..aa9ebaec
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_object_followed_by_closing_object.json
@@ -0,0 +1 @@
+{}} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_object_unclosed_no_value.json b/formats/json/jvmTest/resources/spec_cases/n_structure_object_unclosed_no_value.json
new file mode 100644
index 00000000..17d04514
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_object_unclosed_no_value.json
@@ -0,0 +1 @@
+{"": \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_object_with_comment.json b/formats/json/jvmTest/resources/spec_cases/n_structure_object_with_comment.json
new file mode 100644
index 00000000..ed1b569b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_object_with_comment.json
@@ -0,0 +1 @@
+{"a":/*comment*/"b"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_object_with_trailing_garbage.json b/formats/json/jvmTest/resources/spec_cases/n_structure_object_with_trailing_garbage.json
new file mode 100644
index 00000000..9ca2336d
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_object_with_trailing_garbage.json
@@ -0,0 +1 @@
+{"a": true} "x" \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_apostrophe.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_apostrophe.json
new file mode 100644
index 00000000..8bebe3af
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_apostrophe.json
@@ -0,0 +1 @@
+[' \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_comma.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_comma.json
new file mode 100644
index 00000000..6295fdc3
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_comma.json
@@ -0,0 +1 @@
+[, \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_object.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_object.json
new file mode 100644
index 00000000..e870445b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_object.json
@@ -0,0 +1 @@
+[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_open_object.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_open_object.json
new file mode 100644
index 00000000..7a63c8c5
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_open_object.json
@@ -0,0 +1 @@
+[{ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_open_string.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_open_string.json
new file mode 100644
index 00000000..9822a6ba
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_open_string.json
@@ -0,0 +1 @@
+["a \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_string.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_string.json
new file mode 100644
index 00000000..42a61936
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_array_string.json
@@ -0,0 +1 @@
+["a" \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_object.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object.json
new file mode 100644
index 00000000..81750b96
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object.json
@@ -0,0 +1 @@
+{ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_close_array.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_close_array.json
new file mode 100755
index 00000000..eebc700a
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_close_array.json
@@ -0,0 +1 @@
+{] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_comma.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_comma.json
new file mode 100644
index 00000000..47bc9106
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_comma.json
@@ -0,0 +1 @@
+{, \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_open_array.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_open_array.json
new file mode 100644
index 00000000..381ede5d
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_open_array.json
@@ -0,0 +1 @@
+{[ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_open_string.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_open_string.json
new file mode 100644
index 00000000..328c30cd
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_open_string.json
@@ -0,0 +1 @@
+{"a \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_string_with_apostrophes.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_string_with_apostrophes.json
new file mode 100644
index 00000000..9dba1709
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_object_string_with_apostrophes.json
@@ -0,0 +1 @@
+{'a' \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_open_open.json b/formats/json/jvmTest/resources/spec_cases/n_structure_open_open.json
new file mode 100644
index 00000000..841fd5f8
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_open_open.json
@@ -0,0 +1 @@
+["\{["\{["\{["\{ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_single_star.json b/formats/json/jvmTest/resources/spec_cases/n_structure_single_star.json
new file mode 100755
index 00000000..f59ec20a
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_single_star.json
@@ -0,0 +1 @@
+* \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_trailing_#.json b/formats/json/jvmTest/resources/spec_cases/n_structure_trailing_#.json
new file mode 100644
index 00000000..89861108
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_trailing_#.json
@@ -0,0 +1 @@
+{"a":"b"}#{} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_uescaped_LF_before_string.json b/formats/json/jvmTest/resources/spec_cases/n_structure_uescaped_LF_before_string.json
new file mode 100755
index 00000000..df2f0f24
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_uescaped_LF_before_string.json
@@ -0,0 +1 @@
+[\u000A""] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array.json b/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array.json
new file mode 100755
index 00000000..11209515
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array.json
@@ -0,0 +1 @@
+[1 \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_partial_null.json b/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_partial_null.json
new file mode 100644
index 00000000..0d591762
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_partial_null.json
@@ -0,0 +1 @@
+[ false, nul \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_false.json b/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_false.json
new file mode 100644
index 00000000..a2ff8504
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_false.json
@@ -0,0 +1 @@
+[ true, fals \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_true.json b/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_true.json
new file mode 100644
index 00000000..3149e8f5
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_true.json
@@ -0,0 +1 @@
+[ false, tru \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_object.json b/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_object.json
new file mode 100755
index 00000000..694d69db
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_unclosed_object.json
@@ -0,0 +1 @@
+{"asd":"asd" \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_unicode-identifier.json b/formats/json/jvmTest/resources/spec_cases/n_structure_unicode-identifier.json
new file mode 100644
index 00000000..7284aea3
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_unicode-identifier.json
@@ -0,0 +1 @@
+Ã¥ \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_whitespace_U+2060_word_joiner.json b/formats/json/jvmTest/resources/spec_cases/n_structure_whitespace_U+2060_word_joiner.json
new file mode 100755
index 00000000..81156a69
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_whitespace_U+2060_word_joiner.json
@@ -0,0 +1 @@
+[â ] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/n_structure_whitespace_formfeed.json b/formats/json/jvmTest/resources/spec_cases/n_structure_whitespace_formfeed.json
new file mode 100755
index 00000000..a9ea535d
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/n_structure_whitespace_formfeed.json
@@ -0,0 +1 @@
+[ ] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_array_arraysWithSpaces.json b/formats/json/jvmTest/resources/spec_cases/y_array_arraysWithSpaces.json
new file mode 100755
index 00000000..58229079
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_array_arraysWithSpaces.json
@@ -0,0 +1 @@
+[[] ] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_array_empty-string.json b/formats/json/jvmTest/resources/spec_cases/y_array_empty-string.json
new file mode 100644
index 00000000..93b6be2b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_array_empty-string.json
@@ -0,0 +1 @@
+[""] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_array_empty.json b/formats/json/jvmTest/resources/spec_cases/y_array_empty.json
new file mode 100755
index 00000000..0637a088
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_array_empty.json
@@ -0,0 +1 @@
+[] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_array_ending_with_newline.json b/formats/json/jvmTest/resources/spec_cases/y_array_ending_with_newline.json
new file mode 100755
index 00000000..eac5f7b4
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_array_ending_with_newline.json
@@ -0,0 +1 @@
+["a"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_array_false.json b/formats/json/jvmTest/resources/spec_cases/y_array_false.json
new file mode 100644
index 00000000..67b2f076
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_array_false.json
@@ -0,0 +1 @@
+[false] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_array_heterogeneous.json b/formats/json/jvmTest/resources/spec_cases/y_array_heterogeneous.json
new file mode 100755
index 00000000..d3c1e264
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_array_heterogeneous.json
@@ -0,0 +1 @@
+[null, 1, "1", {}] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_array_null.json b/formats/json/jvmTest/resources/spec_cases/y_array_null.json
new file mode 100644
index 00000000..500db4a8
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_array_null.json
@@ -0,0 +1 @@
+[null] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_array_with_1_and_newline.json b/formats/json/jvmTest/resources/spec_cases/y_array_with_1_and_newline.json
new file mode 100644
index 00000000..99482550
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_array_with_1_and_newline.json
@@ -0,0 +1,2 @@
+[1
+] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_array_with_leading_space.json b/formats/json/jvmTest/resources/spec_cases/y_array_with_leading_space.json
new file mode 100755
index 00000000..18bfe642
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_array_with_leading_space.json
@@ -0,0 +1 @@
+ [1] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_array_with_several_null.json b/formats/json/jvmTest/resources/spec_cases/y_array_with_several_null.json
new file mode 100755
index 00000000..99f6c5d1
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_array_with_several_null.json
@@ -0,0 +1 @@
+[1,null,null,null,2] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_array_with_trailing_space.json b/formats/json/jvmTest/resources/spec_cases/y_array_with_trailing_space.json
new file mode 100755
index 00000000..de9e7a94
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_array_with_trailing_space.json
@@ -0,0 +1 @@
+[2] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number.json b/formats/json/jvmTest/resources/spec_cases/y_number.json
new file mode 100644
index 00000000..e5f5cc33
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number.json
@@ -0,0 +1 @@
+[123e65] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_0e+1.json b/formats/json/jvmTest/resources/spec_cases/y_number_0e+1.json
new file mode 100755
index 00000000..d1d39670
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_0e+1.json
@@ -0,0 +1 @@
+[0e+1] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_0e1.json b/formats/json/jvmTest/resources/spec_cases/y_number_0e1.json
new file mode 100755
index 00000000..3283a793
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_0e1.json
@@ -0,0 +1 @@
+[0e1] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_after_space.json b/formats/json/jvmTest/resources/spec_cases/y_number_after_space.json
new file mode 100644
index 00000000..623570d9
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_after_space.json
@@ -0,0 +1 @@
+[ 4] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_double_close_to_zero.json b/formats/json/jvmTest/resources/spec_cases/y_number_double_close_to_zero.json
new file mode 100755
index 00000000..96555ff7
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_double_close_to_zero.json
@@ -0,0 +1 @@
+[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_int_with_exp.json b/formats/json/jvmTest/resources/spec_cases/y_number_int_with_exp.json
new file mode 100755
index 00000000..a4ca9e75
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_int_with_exp.json
@@ -0,0 +1 @@
+[20e1] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_minus_zero.json b/formats/json/jvmTest/resources/spec_cases/y_number_minus_zero.json
new file mode 100755
index 00000000..37af1312
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_minus_zero.json
@@ -0,0 +1 @@
+[-0] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_negative_int.json b/formats/json/jvmTest/resources/spec_cases/y_number_negative_int.json
new file mode 100644
index 00000000..8e30f8bd
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_negative_int.json
@@ -0,0 +1 @@
+[-123] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_negative_one.json b/formats/json/jvmTest/resources/spec_cases/y_number_negative_one.json
new file mode 100644
index 00000000..99d21a2a
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_negative_one.json
@@ -0,0 +1 @@
+[-1] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_negative_zero.json b/formats/json/jvmTest/resources/spec_cases/y_number_negative_zero.json
new file mode 100644
index 00000000..37af1312
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_negative_zero.json
@@ -0,0 +1 @@
+[-0] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e.json b/formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e.json
new file mode 100644
index 00000000..6edbdfcb
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e.json
@@ -0,0 +1 @@
+[1E22] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e_neg_exp.json b/formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e_neg_exp.json
new file mode 100644
index 00000000..0a01bd3e
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e_neg_exp.json
@@ -0,0 +1 @@
+[1E-2] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e_pos_exp.json b/formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e_pos_exp.json
new file mode 100644
index 00000000..5a8fc097
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_real_capital_e_pos_exp.json
@@ -0,0 +1 @@
+[1E+2] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_real_exponent.json b/formats/json/jvmTest/resources/spec_cases/y_number_real_exponent.json
new file mode 100644
index 00000000..da2522d6
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_real_exponent.json
@@ -0,0 +1 @@
+[123e45] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_real_fraction_exponent.json b/formats/json/jvmTest/resources/spec_cases/y_number_real_fraction_exponent.json
new file mode 100644
index 00000000..3944a7a4
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_real_fraction_exponent.json
@@ -0,0 +1 @@
+[123.456e78] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_real_neg_exp.json b/formats/json/jvmTest/resources/spec_cases/y_number_real_neg_exp.json
new file mode 100644
index 00000000..ca40d3c2
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_real_neg_exp.json
@@ -0,0 +1 @@
+[1e-2] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_real_pos_exponent.json b/formats/json/jvmTest/resources/spec_cases/y_number_real_pos_exponent.json
new file mode 100644
index 00000000..343601d5
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_real_pos_exponent.json
@@ -0,0 +1 @@
+[1e+2] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_simple_int.json b/formats/json/jvmTest/resources/spec_cases/y_number_simple_int.json
new file mode 100644
index 00000000..e47f69af
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_simple_int.json
@@ -0,0 +1 @@
+[123] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_number_simple_real.json b/formats/json/jvmTest/resources/spec_cases/y_number_simple_real.json
new file mode 100644
index 00000000..b02878e5
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_number_simple_real.json
@@ -0,0 +1 @@
+[123.456789] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object.json b/formats/json/jvmTest/resources/spec_cases/y_object.json
new file mode 100755
index 00000000..78262eda
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object.json
@@ -0,0 +1 @@
+{"asd":"sdf", "dfg":"fgh"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object_basic.json b/formats/json/jvmTest/resources/spec_cases/y_object_basic.json
new file mode 100755
index 00000000..646bbe7b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object_basic.json
@@ -0,0 +1 @@
+{"asd":"sdf"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object_duplicated_key.json b/formats/json/jvmTest/resources/spec_cases/y_object_duplicated_key.json
new file mode 100755
index 00000000..bbc2e1ce
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object_duplicated_key.json
@@ -0,0 +1 @@
+{"a":"b","a":"c"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object_duplicated_key_and_value.json b/formats/json/jvmTest/resources/spec_cases/y_object_duplicated_key_and_value.json
new file mode 100755
index 00000000..211581c2
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object_duplicated_key_and_value.json
@@ -0,0 +1 @@
+{"a":"b","a":"b"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object_empty.json b/formats/json/jvmTest/resources/spec_cases/y_object_empty.json
new file mode 100644
index 00000000..9e26dfee
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object_empty.json
@@ -0,0 +1 @@
+{} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object_empty_key.json b/formats/json/jvmTest/resources/spec_cases/y_object_empty_key.json
new file mode 100755
index 00000000..c0013d3b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object_empty_key.json
@@ -0,0 +1 @@
+{"":0} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object_escaped_null_in_key.json b/formats/json/jvmTest/resources/spec_cases/y_object_escaped_null_in_key.json
new file mode 100644
index 00000000..593f0f67
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object_escaped_null_in_key.json
@@ -0,0 +1 @@
+{"foo\u0000bar": 42} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object_extreme_numbers.json b/formats/json/jvmTest/resources/spec_cases/y_object_extreme_numbers.json
new file mode 100644
index 00000000..a0d3531c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object_extreme_numbers.json
@@ -0,0 +1 @@
+{ "min": -1.0e+28, "max": 1.0e+28 } \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object_long_strings.json b/formats/json/jvmTest/resources/spec_cases/y_object_long_strings.json
new file mode 100644
index 00000000..bdc4a087
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object_long_strings.json
@@ -0,0 +1 @@
+{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object_simple.json b/formats/json/jvmTest/resources/spec_cases/y_object_simple.json
new file mode 100644
index 00000000..dacac917
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object_simple.json
@@ -0,0 +1 @@
+{"a":[]} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object_string_unicode.json b/formats/json/jvmTest/resources/spec_cases/y_object_string_unicode.json
new file mode 100644
index 00000000..8effdb29
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object_string_unicode.json
@@ -0,0 +1 @@
+{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" } \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_object_with_newlines.json b/formats/json/jvmTest/resources/spec_cases/y_object_with_newlines.json
new file mode 100644
index 00000000..246ec6b3
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_object_with_newlines.json
@@ -0,0 +1,3 @@
+{
+"a": "b"
+} \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_1_2_3_bytes_UTF-8_sequences.json b/formats/json/jvmTest/resources/spec_cases/y_string_1_2_3_bytes_UTF-8_sequences.json
new file mode 100755
index 00000000..9967ddeb
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_1_2_3_bytes_UTF-8_sequences.json
@@ -0,0 +1 @@
+["\u0060\u012a\u12AB"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pair.json b/formats/json/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pair.json
new file mode 100755
index 00000000..996875cc
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pair.json
@@ -0,0 +1 @@
+["\uD801\udc37"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pairs.json b/formats/json/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pairs.json
new file mode 100755
index 00000000..3401021e
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pairs.json
@@ -0,0 +1 @@
+["\ud83d\ude39\ud83d\udc8d"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_allowed_escapes.json b/formats/json/jvmTest/resources/spec_cases/y_string_allowed_escapes.json
new file mode 100644
index 00000000..7f495532
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_allowed_escapes.json
@@ -0,0 +1 @@
+["\"\\\/\b\f\n\r\t"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_backslash_and_u_escaped_zero.json b/formats/json/jvmTest/resources/spec_cases/y_string_backslash_and_u_escaped_zero.json
new file mode 100755
index 00000000..d4439eda
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_backslash_and_u_escaped_zero.json
@@ -0,0 +1 @@
+["\\u0000"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_backslash_doublequotes.json b/formats/json/jvmTest/resources/spec_cases/y_string_backslash_doublequotes.json
new file mode 100644
index 00000000..ae03243b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_backslash_doublequotes.json
@@ -0,0 +1 @@
+["\""] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_comments.json b/formats/json/jvmTest/resources/spec_cases/y_string_comments.json
new file mode 100644
index 00000000..2260c20c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_comments.json
@@ -0,0 +1 @@
+["a/*b*/c/*d//e"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_double_escape_a.json b/formats/json/jvmTest/resources/spec_cases/y_string_double_escape_a.json
new file mode 100644
index 00000000..6715d6f4
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_double_escape_a.json
@@ -0,0 +1 @@
+["\\a"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_double_escape_n.json b/formats/json/jvmTest/resources/spec_cases/y_string_double_escape_n.json
new file mode 100644
index 00000000..44ca56c4
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_double_escape_n.json
@@ -0,0 +1 @@
+["\\n"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_escaped_control_character.json b/formats/json/jvmTest/resources/spec_cases/y_string_escaped_control_character.json
new file mode 100644
index 00000000..5b014a9c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_escaped_control_character.json
@@ -0,0 +1 @@
+["\u0012"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_escaped_noncharacter.json b/formats/json/jvmTest/resources/spec_cases/y_string_escaped_noncharacter.json
new file mode 100755
index 00000000..2ff52e2c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_escaped_noncharacter.json
@@ -0,0 +1 @@
+["\uFFFF"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_in_array.json b/formats/json/jvmTest/resources/spec_cases/y_string_in_array.json
new file mode 100755
index 00000000..21d7ae4c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_in_array.json
@@ -0,0 +1 @@
+["asd"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_in_array_with_leading_space.json b/formats/json/jvmTest/resources/spec_cases/y_string_in_array_with_leading_space.json
new file mode 100755
index 00000000..9e1887c1
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_in_array_with_leading_space.json
@@ -0,0 +1 @@
+[ "asd"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_last_surrogates_1_and_2.json b/formats/json/jvmTest/resources/spec_cases/y_string_last_surrogates_1_and_2.json
new file mode 100644
index 00000000..3919cef7
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_last_surrogates_1_and_2.json
@@ -0,0 +1 @@
+["\uDBFF\uDFFF"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_nbsp_uescaped.json b/formats/json/jvmTest/resources/spec_cases/y_string_nbsp_uescaped.json
new file mode 100644
index 00000000..2085ab1a
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_nbsp_uescaped.json
@@ -0,0 +1 @@
+["new\u00A0line"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+10FFFF.json b/formats/json/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+10FFFF.json
new file mode 100755
index 00000000..059e4d9d
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+10FFFF.json
@@ -0,0 +1 @@
+["ô¿¿"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+FFFF.json b/formats/json/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+FFFF.json
new file mode 100755
index 00000000..4c913bd4
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+FFFF.json
@@ -0,0 +1 @@
+["ï¿¿"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_null_escape.json b/formats/json/jvmTest/resources/spec_cases/y_string_null_escape.json
new file mode 100644
index 00000000..c1ad8440
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_null_escape.json
@@ -0,0 +1 @@
+["\u0000"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_one-byte-utf-8.json b/formats/json/jvmTest/resources/spec_cases/y_string_one-byte-utf-8.json
new file mode 100644
index 00000000..15718592
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_one-byte-utf-8.json
@@ -0,0 +1 @@
+["\u002c"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_pi.json b/formats/json/jvmTest/resources/spec_cases/y_string_pi.json
new file mode 100644
index 00000000..9df11ae8
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_pi.json
@@ -0,0 +1 @@
+["Ï€"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_reservedCharacterInUTF-8_U+1BFFF.json b/formats/json/jvmTest/resources/spec_cases/y_string_reservedCharacterInUTF-8_U+1BFFF.json
new file mode 100755
index 00000000..10a33a17
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_reservedCharacterInUTF-8_U+1BFFF.json
@@ -0,0 +1 @@
+["𛿿"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_simple_ascii.json b/formats/json/jvmTest/resources/spec_cases/y_string_simple_ascii.json
new file mode 100644
index 00000000..8cadf7d0
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_simple_ascii.json
@@ -0,0 +1 @@
+["asd "] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_space.json b/formats/json/jvmTest/resources/spec_cases/y_string_space.json
new file mode 100644
index 00000000..efd782cc
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_space.json
@@ -0,0 +1 @@
+" " \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json b/formats/json/jvmTest/resources/spec_cases/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json
new file mode 100755
index 00000000..7620b665
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json
@@ -0,0 +1 @@
+["\uD834\uDd1e"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_three-byte-utf-8.json b/formats/json/jvmTest/resources/spec_cases/y_string_three-byte-utf-8.json
new file mode 100644
index 00000000..108f1d67
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_three-byte-utf-8.json
@@ -0,0 +1 @@
+["\u0821"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_two-byte-utf-8.json b/formats/json/jvmTest/resources/spec_cases/y_string_two-byte-utf-8.json
new file mode 100644
index 00000000..461503c3
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_two-byte-utf-8.json
@@ -0,0 +1 @@
+["\u0123"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_u+2028_line_sep.json b/formats/json/jvmTest/resources/spec_cases/y_string_u+2028_line_sep.json
new file mode 100755
index 00000000..897b6021
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_u+2028_line_sep.json
@@ -0,0 +1 @@
+["
"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_u+2029_par_sep.json b/formats/json/jvmTest/resources/spec_cases/y_string_u+2029_par_sep.json
new file mode 100755
index 00000000..8cd998c8
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_u+2029_par_sep.json
@@ -0,0 +1 @@
+["
"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_uEscape.json b/formats/json/jvmTest/resources/spec_cases/y_string_uEscape.json
new file mode 100755
index 00000000..f7b41a02
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_uEscape.json
@@ -0,0 +1 @@
+["\u0061\u30af\u30EA\u30b9"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_uescaped_newline.json b/formats/json/jvmTest/resources/spec_cases/y_string_uescaped_newline.json
new file mode 100644
index 00000000..3a5a220b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_uescaped_newline.json
@@ -0,0 +1 @@
+["new\u000Aline"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_unescaped_char_delete.json b/formats/json/jvmTest/resources/spec_cases/y_string_unescaped_char_delete.json
new file mode 100755
index 00000000..7d064f49
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_unescaped_char_delete.json
@@ -0,0 +1 @@
+[""] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_unicode.json b/formats/json/jvmTest/resources/spec_cases/y_string_unicode.json
new file mode 100644
index 00000000..3598095b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_unicode.json
@@ -0,0 +1 @@
+["\uA66D"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_unicodeEscapedBackslash.json b/formats/json/jvmTest/resources/spec_cases/y_string_unicodeEscapedBackslash.json
new file mode 100755
index 00000000..0bb3b51e
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_unicodeEscapedBackslash.json
@@ -0,0 +1 @@
+["\u005C"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_unicode_2.json b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_2.json
new file mode 100644
index 00000000..a7dcb976
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_2.json
@@ -0,0 +1 @@
+["â‚㈴â‚"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+10FFFE_nonchar.json b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+10FFFE_nonchar.json
new file mode 100644
index 00000000..9a8370b9
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+10FFFE_nonchar.json
@@ -0,0 +1 @@
+["\uDBFF\uDFFE"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+1FFFE_nonchar.json b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+1FFFE_nonchar.json
new file mode 100644
index 00000000..c51f8ae4
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+1FFFE_nonchar.json
@@ -0,0 +1 @@
+["\uD83F\uDFFE"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json
new file mode 100644
index 00000000..626d5f81
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json
@@ -0,0 +1 @@
+["\u200B"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+2064_invisible_plus.json b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+2064_invisible_plus.json
new file mode 100644
index 00000000..1e23972c
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+2064_invisible_plus.json
@@ -0,0 +1 @@
+["\u2064"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+FDD0_nonchar.json b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+FDD0_nonchar.json
new file mode 100644
index 00000000..18ef151b
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+FDD0_nonchar.json
@@ -0,0 +1 @@
+["\uFDD0"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+FFFE_nonchar.json b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+FFFE_nonchar.json
new file mode 100644
index 00000000..13d261fd
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_U+FFFE_nonchar.json
@@ -0,0 +1 @@
+["\uFFFE"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_unicode_escaped_double_quote.json b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_escaped_double_quote.json
new file mode 100755
index 00000000..4e625785
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_unicode_escaped_double_quote.json
@@ -0,0 +1 @@
+["\u0022"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_utf8.json b/formats/json/jvmTest/resources/spec_cases/y_string_utf8.json
new file mode 100644
index 00000000..40878435
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_utf8.json
@@ -0,0 +1 @@
+["€ð„ž"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_string_with_del_character.json b/formats/json/jvmTest/resources/spec_cases/y_string_with_del_character.json
new file mode 100755
index 00000000..8bd24907
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_string_with_del_character.json
@@ -0,0 +1 @@
+["aa"] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_false.json b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_false.json
new file mode 100644
index 00000000..02e4a84d
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_false.json
@@ -0,0 +1 @@
+false \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_int.json b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_int.json
new file mode 100755
index 00000000..f70d7bba
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_int.json
@@ -0,0 +1 @@
+42 \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_negative_real.json b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_negative_real.json
new file mode 100755
index 00000000..b5135a20
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_negative_real.json
@@ -0,0 +1 @@
+-0.1 \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_null.json b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_null.json
new file mode 100644
index 00000000..ec747fa4
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_null.json
@@ -0,0 +1 @@
+null \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_string.json b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_string.json
new file mode 100755
index 00000000..b6e982ca
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_string.json
@@ -0,0 +1 @@
+"asd" \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_true.json b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_true.json
new file mode 100755
index 00000000..f32a5804
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_structure_lonely_true.json
@@ -0,0 +1 @@
+true \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_structure_string_empty.json b/formats/json/jvmTest/resources/spec_cases/y_structure_string_empty.json
new file mode 100644
index 00000000..3cc762b5
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_structure_string_empty.json
@@ -0,0 +1 @@
+"" \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_structure_trailing_newline.json b/formats/json/jvmTest/resources/spec_cases/y_structure_trailing_newline.json
new file mode 100644
index 00000000..0c3426d4
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_structure_trailing_newline.json
@@ -0,0 +1 @@
+["a"]
diff --git a/formats/json/jvmTest/resources/spec_cases/y_structure_true_in_array.json b/formats/json/jvmTest/resources/spec_cases/y_structure_true_in_array.json
new file mode 100644
index 00000000..de601e30
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_structure_true_in_array.json
@@ -0,0 +1 @@
+[true] \ No newline at end of file
diff --git a/formats/json/jvmTest/resources/spec_cases/y_structure_whitespace_array.json b/formats/json/jvmTest/resources/spec_cases/y_structure_whitespace_array.json
new file mode 100644
index 00000000..2bedf7f2
--- /dev/null
+++ b/formats/json/jvmTest/resources/spec_cases/y_structure_whitespace_array.json
@@ -0,0 +1 @@
+ [] \ No newline at end of file
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/JavaCollectionsTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/JavaCollectionsTest.kt
new file mode 100644
index 00000000..a0ff55a2
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/JavaCollectionsTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.json.Json
+import org.junit.Test
+import java.util.HashMap
+import java.util.HashSet
+import kotlin.collections.LinkedHashMap
+import kotlin.collections.Map
+import kotlin.collections.hashMapOf
+import kotlin.collections.hashSetOf
+import kotlin.test.assertEquals
+
+
+class JavaCollectionsTest {
+ @Serializable
+ data class HasHashMap(
+ val s: String,
+ val hashMap: HashMap<Int, String>,
+ val hashSet: HashSet<Int>,
+ val linkedHashMap: LinkedHashMap<Int, String>,
+ val kEntry: Map.Entry<Int, String>?
+ )
+
+ @Test
+ fun test() {
+ val original = HasHashMap("42", hashMapOf(1 to "1", 2 to "2"), hashSetOf(11), LinkedHashMap(), null)
+ val serializer = HasHashMap.serializer()
+ val string = Json.encodeToString(serializer = serializer, value = original)
+ assertEquals(
+ expected = """{"s":"42","hashMap":{"1":"1","2":"2"},"hashSet":[11],"linkedHashMap":{},"kEntry":null}""",
+ actual = string
+ )
+ val restored = Json.decodeFromString(deserializer = serializer, string = string)
+ assertEquals(expected = original, actual = restored)
+ }
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/JvmMissingFieldsExceptionTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/JvmMissingFieldsExceptionTest.kt
new file mode 100644
index 00000000..b932b5ae
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/JvmMissingFieldsExceptionTest.kt
@@ -0,0 +1,142 @@
+package kotlinx.serialization
+
+import org.junit.Test
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.modules.SerializersModule
+import kotlinx.serialization.modules.polymorphic
+import kotlinx.serialization.modules.subclass
+import org.junit.Ignore
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.assertTrue
+
+class JvmMissingFieldsExceptionTest {
+ @Serializable
+ data class Generic<out T1, out T2, out T3>(val f1: T1, val f2: T2, val f3: T3)
+
+ @Serializable
+ sealed class Parent(val p1: Int, val p2: Int = 2, val p3: Int) {
+ @Serializable
+ @SerialName("child")
+ data class Child(val c1: Int = 1, val c2: Int, val c3: Int = 3) : Parent(c1 + 1, 2, 3)
+ }
+
+ @Serializable
+ open class ShortPlaneClass(val f1: Int, val f2: Int, val f3: Int = 3, val f4: Int)
+
+ @Serializable
+ class WithTransient(val f1: Int, @Transient val f2: Int = 2, val f3: Int, val f4: Int)
+
+ @Serializable
+ abstract class SimpleAbstract(val p1: Int, val p2: Int)
+
+ @Serializable
+ @SerialName("a")
+ data class ChildA(val c1: Int, val c2: Int = 2, val c3: Int) : SimpleAbstract(0, 10)
+
+ @Serializable
+ data class PolymorphicWrapper(@Polymorphic val nested: SimpleAbstract)
+
+ @Serializable
+ class BigPlaneClass(
+ val f0: Int,
+ val f5: Int = 5,
+ val f6: Int,
+ val f7: Int = 7,
+ val f8: Int,
+ val f9: Int,
+ val f10: Int,
+ val f11: Int,
+ val f12: Int,
+ val f13: Int,
+ val f14: Int,
+ val f15: Int,
+ val f16: Int,
+ val f17: Int,
+ val f18: Int,
+ val f19: Int,
+ val f20: Int,
+ val f21: Int,
+ val f22: Int,
+ val f23: Int,
+ val f24: Int,
+ val f25: Int,
+ val f26: Int,
+ val f27: Int,
+ val f28: Int,
+ val f29: Int,
+ val f30: Int,
+ val f31: Int,
+ val f32: Int,
+ val f33: Int,
+ val f34: Int,
+ val f35: Int,
+ ) : ShortPlaneClass(1, 2, 3, 4)
+
+ @Test
+ fun testShortPlaneClass() {
+ assertFailsWithMessages(listOf("f2", "f4")) {
+ Json.decodeFromString<ShortPlaneClass>("""{"f1":1}""")
+ }
+ }
+
+ @Test
+ fun testBigPlaneClass() {
+ val missedFields = MutableList(35) { "f$it" }
+ val definedInJsonFields = arrayOf("f1", "f15", "f34")
+ val optionalFields = arrayOf("f3", "f5", "f7")
+ missedFields.removeAll(definedInJsonFields)
+ missedFields.removeAll(optionalFields)
+ assertFailsWithMessages(missedFields) {
+ Json.decodeFromString<BigPlaneClass>("""{"f1":1, "f15": 15, "f34": 34}""")
+ }
+ }
+
+ @Test
+ fun testAnnotatedPolymorphic() {
+ val module = SerializersModule {
+ polymorphic(SimpleAbstract::class, null) {
+ subclass(ChildA::class)
+ }
+ }
+
+ assertFailsWithMessages(listOf("p2", "c3")) {
+ Json {
+ serializersModule = module
+ useArrayPolymorphism = false
+ }.decodeFromString<PolymorphicWrapper>("""{"nested": {"type": "a", "p1": 1, "c1": 11}}""")
+ }
+ }
+
+
+ @Test
+ fun testSealed() {
+ assertFailsWithMessages(listOf("p3", "c2")) {
+ Json { useArrayPolymorphism = false }
+ .decodeFromString<Parent>("""{"type": "child", "p1":1, "c1": 11}""")
+ }
+ }
+
+ @Test
+ fun testTransient() {
+ assertFailsWithMessages(listOf("f3", "f4")) {
+ Json { useArrayPolymorphism = false }
+ .decodeFromString<WithTransient>("""{"f1":1}""")
+ }
+ }
+
+ @Test
+ fun testGeneric() {
+ assertFailsWithMessages(listOf("f2", "f3")) {
+ Json.decodeFromString<Generic<Int, Int, Int>>("""{"f1":1}""")
+ }
+ }
+
+
+ private inline fun assertFailsWithMessages(messages: List<String>, block: () -> Unit) {
+ val exception = assertFailsWith(SerializationException::class, null, block)
+ assertEquals("kotlinx.serialization.MissingFieldException", exception::class.qualifiedName)
+ val missedMessages = messages.filter { !exception.message!!.contains(it) }
+ assertTrue(missedMessages.isEmpty(), "Expected message '${exception.message}' to contain substrings $missedMessages")
+ }
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/SerializationCasesTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/SerializationCasesTest.kt
new file mode 100644
index 00000000..cbef36f3
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/SerializationCasesTest.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2018 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.json.*
+import org.junit.*
+import org.junit.Assert.*
+
+class SerializationCasesTest : JsonTestBase() {
+
+ @Serializable
+ data class Data1(val a: Int, val b: Int)
+
+ @Serializer(forClass = Data1::class)
+ object ExtDataSerializer1
+
+ @Test
+ fun testConstructorValProperties() {
+ val data = Data1(1, 2)
+
+ // Serialize with internal serializer for Data class
+ assertEquals("""{"a":1,"b":2}""", default.encodeToString(data))
+ assertEquals(data, Json.decodeFromString<Data1>("""{"a":1,"b":2}"""))
+
+ // Serialize with external serializer for Data class
+ assertEquals("""{"a":1,"b":2}""", default.encodeToString(ExtDataSerializer1, data))
+ assertEquals(data, Json.decodeFromString(ExtDataSerializer1, """{"a":1,"b":2}"""))
+ }
+
+ @Serializable
+ class Data2 {
+ var a = 0
+ var b = 0
+ override fun equals(other: Any?) = other is Data2 && other.a == a && other.b == b
+ }
+
+ @Serializer(forClass=Data2::class)
+ object ExtDataSerializer2
+
+ @Test
+ fun testBodyVarProperties() {
+ val data = Data2().apply {
+ a = 1
+ b = 2
+ }
+
+ // Serialize with internal serializer for Data class
+ assertEquals("""{"a":1,"b":2}""", default.encodeToString(data))
+ assertEquals(data, Json.decodeFromString<Data2>("""{"a":1,"b":2}"""))
+
+ // Serialize with external serializer for Data class
+ assertEquals("""{"a":1,"b":2}""", default.encodeToString(ExtDataSerializer2, data))
+ assertEquals(data, Json.decodeFromString(ExtDataSerializer2, """{"a":1,"b":2}"""))
+ }
+
+ enum class TintEnum { LIGHT, DARK }
+
+ @Serializable
+ data class Data3(
+ val a: String,
+ val b: List<Int>,
+ val c: Map<String, TintEnum>
+ )
+
+ // Serialize with external serializer for Data class
+ @Serializer(forClass = Data3::class)
+ object ExtDataSerializer3
+
+ @Test
+ fun testNestedValues() {
+ val data = Data3("Str", listOf(1, 2), mapOf("lt" to TintEnum.LIGHT, "dk" to TintEnum.DARK))
+ // Serialize with internal serializer for Data class
+ val expected = """{"a":"Str","b":[1,2],"c":{"lt":"LIGHT","dk":"DARK"}}"""
+ assertEquals(expected, default.encodeToString(data))
+ assertEquals(data, Json.decodeFromString<Data3>(expected))
+ assertEquals(expected, default.encodeToString(ExtDataSerializer3, data))
+ assertEquals(data, Json.decodeFromString(ExtDataSerializer3, expected))
+ }
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/SerializeJavaClassTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/SerializeJavaClassTest.kt
new file mode 100644
index 00000000..db79b477
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/SerializeJavaClassTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.Json
+import org.junit.Test
+import java.text.DateFormat
+import java.text.SimpleDateFormat
+import java.util.*
+import kotlin.test.assertEquals
+
+@Serializer(forClass = Date::class)
+object DateSerializer : KSerializer<Date> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("java.util.Date", PrimitiveKind.STRING)
+
+ // Consider wrapping in ThreadLocal if serialization may happen in multiple threads
+ private val df: DateFormat = SimpleDateFormat("dd/MM/yyyy HH:mm:ss.SSS").apply {
+ timeZone = TimeZone.getTimeZone("GMT+2")
+ }
+
+ override fun serialize(encoder: Encoder, value: Date) {
+ encoder.encodeString(df.format(value))
+ }
+
+ override fun deserialize(decoder: Decoder): Date {
+ return df.parse(decoder.decodeString())
+ }
+}
+
+@Serializable
+data class ClassWithDate(@Serializable(with = DateSerializer::class) val date: Date)
+
+class SerializeJavaClassTest {
+ @Test
+ fun serializeToStringAndRestore() {
+ // Thursday, 4 October 2018 09:00:00 GMT+02:00 — KotlinConf 2018 Keynote
+ val date = ClassWithDate(Date(1538636400000L))
+ val s = Json.encodeToString(date)
+ assertEquals("""{"date":"04/10/2018 09:00:00.000"}""", s)
+ val date2 = Json.decodeFromString(ClassWithDate.serializer(), s)
+ assertEquals(date, date2)
+ }
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/SerializerForNullableJavaTypeTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/SerializerForNullableJavaTypeTest.kt
new file mode 100644
index 00000000..ebed6f37
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/SerializerForNullableJavaTypeTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import org.junit.Test
+import java.util.*
+import kotlin.test.*
+
+class SerializerForNullableJavaTypeTest {
+
+ // User-reported generic serialization
+ class DateSerializer : KSerializer<Date?> {
+
+ override val descriptor = PrimitiveSerialDescriptor("LocalTime?", PrimitiveKind.LONG).nullable
+
+ override fun deserialize(decoder: Decoder): Date? = when (val seconds = decoder.decodeLong()) {
+ -1L -> null
+ else -> Date(seconds)
+ }
+
+ override fun serialize(encoder: Encoder, value: Date?) {
+ when (value) {
+ null -> encoder.encodeLong(-1L) //this line is never reached despite that nulls exist in serialized lists
+ else -> encoder.encodeLong(value.toInstant().toEpochMilli())
+ }
+ }
+ }
+
+ @Serializable
+ private data class ListWrapper(val times: List<@Serializable(with = DateSerializer::class) Date?>)
+
+ @Test
+ fun testMixedList() {
+ val data = ListWrapper(listOf(Date(42), null))
+ val str = Json.encodeToString(data)
+ assertEquals("""{"times":[42,-1]}""", str)
+ assertEquals(data, Json.decodeFromString(str))
+ }
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt
new file mode 100644
index 00000000..e6fbecb9
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.coroutines.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class StacktraceRecoveryTest {
+ @Serializable
+ private class Data(val s: String)
+
+ private class BadDecoder : AbstractDecoder() {
+ override val serializersModule: SerializersModule = EmptySerializersModule
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int = 42
+ }
+
+ @Test
+ fun testJsonDecodingException() = checkRecovered<JsonDecodingException> {
+ Json.decodeFromString<String>("42")
+ }
+
+ @Test
+ fun testJsonEncodingException() = checkRecovered<JsonEncodingException> {
+ Json.encodeToString(Double.NaN)
+ }
+
+ @Test
+ // checks simple name because UFE is internal class
+ fun testUnknownFieldException() = checkRecovered("UnknownFieldException") {
+ val serializer = Data.serializer()
+ serializer.deserialize(BadDecoder())
+ }
+
+ @Test
+ // checks simple name because MFE is internal class
+ fun testMissingFieldException() = checkRecovered("MissingFieldException") {
+ Json.decodeFromString<Data>("{}")
+ }
+
+ private fun checkRecovered(exceptionClassSimpleName: String, block: () -> Unit) = runBlocking {
+ val result = runCatching {
+ callBlockWithRecovery(block)
+ }
+ assertTrue(result.isFailure, "Block should have failed")
+ val e = result.exceptionOrNull()!!
+ assertEquals(exceptionClassSimpleName, e::class.simpleName!!)
+ val cause = e.cause
+ assertNotNull(cause, "Exception should have cause: $e")
+ assertEquals(e.message, cause.message)
+ assertEquals(exceptionClassSimpleName, e::class.simpleName!!)
+ }
+
+ private inline fun <reified E : Exception> checkRecovered(noinline block: () -> Unit) = runBlocking {
+ val result = runCatching {
+ callBlockWithRecovery(block)
+ }
+ assertTrue(result.isFailure, "Block should have failed")
+ val e = result.exceptionOrNull()!!
+ assertEquals(E::class, e::class)
+ val cause = e.cause
+ assertNotNull(cause, "Exception should have cause: $e")
+ assertEquals(e.message, cause.message)
+ assertEquals(E::class, cause::class)
+ }
+
+ // KLUDGE: A separate function with state-machine to ensure coroutine DebugMetadata is generated. See KT-41789
+ private suspend fun callBlockWithRecovery(block: () -> Unit) {
+ yield()
+ // use withContext to perform switch between coroutines and thus trigger exception recovery machinery
+ withContext(NonCancellable) {
+ block()
+ }
+ }
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/features/ContextualSerializationOnFileTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/features/ContextualSerializationOnFileTest.kt
new file mode 100644
index 00000000..a190a483
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/features/ContextualSerializationOnFileTest.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+// TODO: Move to common tests after https://youtrack.jetbrains.com/issue/KT-28927 is fixed
+
+@file:UseContextualSerialization(Int::class, IntHolder::class)
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+@Serializable
+data class Carrier3(
+ val a: IntHolder,
+ val i: Int,
+ val nullable: Int?,
+ val nullableIntHolder: IntHolder?,
+ val nullableIntList: List<Int?> = emptyList(),
+ val nullableIntHolderNullableList: List<IntHolder?>? = null
+)
+
+class ContextualSerializationOnFileTest {
+ val module = SerializersModule {
+ contextual(DividingIntSerializer)
+ contextual(MultiplyingIntHolderSerializer)
+ }
+ val json = Json { serializersModule = module; encodeDefaults = true }
+
+ @Test
+ fun testOnFile() {
+ val str = json.encodeToString(Carrier3.serializer(), Carrier3(IntHolder(42), 8, 8, IntHolder(42)))
+ assertEquals(
+ """{"a":84,"i":4,"nullable":4,"nullableIntHolder":84,"nullableIntList":[],"nullableIntHolderNullableList":null}""",
+ str
+ )
+ }
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/features/InternalInheritanceTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/features/InternalInheritanceTest.kt
new file mode 100644
index 00000000..6a1f722e
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/features/InternalInheritanceTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import org.junit.*
+import org.junit.Assert.*
+
+class InternalInheritanceTest : JsonTestBase() {
+ @Serializable
+ open class A(val parent: Int) {
+ private val rootOptional = "rootOptional"
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is A) return false
+
+ if (parent != other.parent) return false
+ if (rootOptional != other.rootOptional) return false
+
+ return true
+ }
+ }
+
+ @Serializable
+ open class B(val parent2: Int, @Transient val transientDerived: String = "X", val derived: String) : A(parent2) {
+ protected val bodyDerived = "body"
+ }
+
+ @Serializable
+ class C(val parent3: Int) : B(parent3, derived = "derived") {
+ val lastDerived = "optional"
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other?.javaClass != javaClass) return false
+
+ other as C
+
+ if (!super.equals(other)) return false
+ if (parent3 != other.parent3) return false
+ if (lastDerived != other.lastDerived) return false
+ if (parent2 != other.parent2) return false
+ if (transientDerived != other.transientDerived) return false
+ if (derived != other.derived) return false
+ if (bodyDerived != other.bodyDerived) return false
+
+ return true
+ }
+ }
+
+ @Test
+ fun testEncodeToString() {
+ assertEquals(
+ """{"parent":42,"rootOptional":"rootOptional","parent2":42,"derived":"derived",""" +
+ """"bodyDerived":"body","parent3":42,"lastDerived":"optional"}""",
+ default.encodeToString(C(42))
+ )
+ assertEquals(
+ """{"parent":13,"rootOptional":"rootOptional","parent2":13,"derived":"bbb","bodyDerived":"body"}""",
+ default.encodeToString(B(13, derived = "bbb"))
+ )
+ }
+
+ @Test
+ fun testParse() {
+ assertEquals(
+ C(42),
+ default.decodeFromString<C>(
+ """{"parent":42,"rootOptional":"rootOptional","parent2":42,""" +
+ """"derived":"derived","bodyDerived":"body","parent3":42,"lastDerived":"optional"}"""
+ )
+ )
+ assertEquals(
+ C(43),
+ default.decodeFromString<C>("""{"parent":43,"rootOptional":"rootOptional","parent2":43,"derived":"derived",""" +
+ """"bodyDerived":"body","parent3":43,"lastDerived":"optional"}""")
+ )
+ }
+
+ @Test
+ fun testParseOptionals() {
+ assertEquals(
+ B(100, derived = "wowstring"),
+ default.decodeFromString<B>("""{"parent":100,"rootOptional":"rootOptional","parent2":100,"derived":"wowstring","bodyDerived":"body"}""")
+ )
+ assertEquals(
+ C(44),
+ default.decodeFromString<C>("""{"parent":44, "parent2":44,"derived":"derived","bodyDerived":"body","parent3":44}""")
+ )
+ assertEquals(
+ B(101, derived = "wowstring"),
+ default.decodeFromString<B>("""{"parent":101,"parent2":101,"derived":"wowstring","bodyDerived":"body"}""")
+ )
+ assertEquals(
+ A(77),
+ default.decodeFromString<A>("""{"parent":77,"rootOptional":"rootOptional"}""")
+ )
+ assertEquals(
+ A(78),
+ default.decodeFromString<A>("""{"parent":78}""")
+ )
+ }
+
+ @Test(expected = SerializationException::class)
+ fun testThrowTransient() {
+ Json.decodeFromString<B>("""{"parent":100,"rootOptional":"rootOptional","transientDerived":"X",""" +
+ """"parent2":100,"derived":"wowstring","bodyDerived":"body"}""")
+ }
+
+ @Test(expected = SerializationException::class)
+ fun testThrowMissingField() {
+ default.decodeFromString<C>("""{"parent":42,"rootOptional":"rootOptional","derived":"derived",""" +
+ """"bodyDerived":"body","parent3":42,"lastDerived":"optional"}""")
+ }
+
+ @Test
+ fun testSerializeAsParent() {
+ val obj1: A = B(77, derived = "derived")
+ val obj2: A = C(77)
+ assertEquals("""{"parent":77,"rootOptional":"rootOptional"}""", default.encodeToString(obj1))
+ assertEquals("""{"parent":77,"rootOptional":"rootOptional"}""", default.encodeToString(obj2))
+ }
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/features/JsonJvmStreamsTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/features/JsonJvmStreamsTest.kt
new file mode 100644
index 00000000..b576a2c1
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/features/JsonJvmStreamsTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.SerializationException
+import kotlinx.serialization.StringData
+import kotlinx.serialization.builtins.serializer
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.internal.BATCH_SIZE
+import kotlinx.serialization.test.*
+import org.junit.Test
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+
+class JsonJvmStreamsTest {
+ private val strLen = BATCH_SIZE * 2 + 42
+
+ @Test
+ fun testParsesStringsLongerThanBuffer() {
+ val str = "a".repeat(strLen)
+ val input = """{"data":"$str"}"""
+ assertEquals(input, Json.encodeViaStream(StringData.serializer(), StringData(str)))
+ assertEquals(str, Json.decodeViaStream(StringData.serializer(), input).data)
+ assertEquals(str, Json.decodeViaStream(String.serializer(), "\"$str\""))
+ }
+
+ @Test
+ fun testSkipsWhitespacesLongerThanBuffer() {
+ val str = "a".repeat(strLen)
+ val ws = " ".repeat(strLen)
+ val input = """{"data":$ws"$str"}"""
+ assertEquals("""{"data":"$str"}""", Json.encodeViaStream(StringData.serializer(), StringData(str)))
+ assertEquals(str, Json.decodeViaStream(StringData.serializer(), input).data)
+ }
+
+ @Test
+ fun testHandlesEscapesLongerThanBuffer() {
+ val str = "\\t".repeat(strLen)
+ val expected = "\t".repeat(strLen)
+ val input = """{"data":"$str"}"""
+ assertEquals(input, Json.encodeViaStream(StringData.serializer(), StringData(expected)))
+ assertEquals(expected, Json.decodeViaStream(StringData.serializer(), input).data)
+ }
+
+ @Test
+ fun testHandlesLongLenientStrings() {
+ val str = "a".repeat(strLen)
+ val input = """{"data":$str}"""
+ val json = Json { isLenient = true }
+ assertEquals(str, json.decodeViaStream(StringData.serializer(), input).data)
+ assertEquals(str, json.decodeViaStream(String.serializer(), str))
+ }
+
+ @Test
+ fun testThrowsCorrectExceptionOnEof() {
+ assertFailsWith<SerializationException> {
+ Json.decodeViaStream(StringData.serializer(), """{"data":""")
+ }
+ assertFailsWith<SerializationException> {
+ Json.decodeViaStream(StringData.serializer(), "")
+ }
+ assertFailsWith<SerializationException> {
+ Json.decodeViaStream(String.serializer(), "\"")
+ }
+ }
+
+ @Test
+ fun testRandomEscapeSequences() {
+ repeat(1000) {
+ val s = generateRandomUnicodeString(strLen)
+ try {
+ val serializer = String.serializer()
+ val b = ByteArrayOutputStream()
+ Json.encodeToStream(serializer, s, b)
+ val restored = Json.decodeFromStream(serializer, ByteArrayInputStream(b.toByteArray()))
+ assertEquals(s, restored)
+ } catch (e: Throwable) {
+ // Not assertion error to preserve cause
+ throw IllegalStateException("Unexpectedly failed test, cause string: $s", e)
+ }
+ }
+ }
+
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/features/JsonLazySequenceTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/features/JsonLazySequenceTest.kt
new file mode 100644
index 00000000..aad9a0f5
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/features/JsonLazySequenceTest.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.runBlocking
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.serializer
+import kotlinx.serialization.features.sealed.SealedChild
+import kotlinx.serialization.features.sealed.SealedParent
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.internal.JsonDecodingException
+import kotlinx.serialization.test.assertFailsWithMessage
+import org.junit.Test
+import java.io.*
+import kotlin.test.*
+
+class JsonLazySequenceTest {
+ val json = Json
+
+ private suspend inline fun <reified T> Flow<T>.writeToStream(os: OutputStream) {
+ collect {
+ json.encodeToStream(it, os)
+ }
+ }
+
+ private suspend inline fun <reified T> Json.readFromStream(iss: InputStream): Flow<T> = flow {
+ val serial = serializer<T>()
+ val iter = iterateOverStream(iss, serial)
+ while (iter.hasNext()) {
+ emit(iter.next())
+ }
+ }.flowOn(Dispatchers.IO)
+
+ private val inputStringWsSeparated = """{"data":"a"}{"data":"b"}{"data":"c"}"""
+ private val inputStringWrapped = """[{"data":"a"},{"data":"b"},{"data":"c"}]"""
+ private val inputList = listOf(StringData("a"), StringData("b"), StringData("c"))
+
+ @Test
+ fun testEncodeSeveralItems() {
+ val items = inputList
+ val os = ByteArrayOutputStream()
+ runBlocking {
+ val f = flow<StringData> { items.forEach { emit(it) } }
+ f.writeToStream(os)
+ }
+
+ assertEquals(inputStringWsSeparated, os.toString(Charsets.UTF_8.name()))
+ }
+
+ @Test
+ fun testDecodeSeveralItems() {
+ val ins = ByteArrayInputStream(inputStringWsSeparated.encodeToByteArray())
+ assertFailsWithMessage<SerializationException>("EOF") {
+ json.decodeFromStream<StringData>(ins)
+ }
+ }
+
+ private inline fun <reified T> Iterator<T>.assertNext(expected: T) {
+ assertTrue(hasNext())
+ assertEquals(expected, next())
+ }
+
+ private fun <T> Json.iterateOverStream(stream: InputStream, deserializer: DeserializationStrategy<T>): Iterator<T> =
+ decodeToSequence(stream, deserializer).iterator()
+
+ private fun withInputs(vararg inputs: String = arrayOf(inputStringWsSeparated, inputStringWrapped), block: (InputStream) -> Unit) {
+ for (input in inputs) {
+ val res = runCatching { block(input.asInputStream()) }
+ if (res.isFailure) throw AssertionError("Failed test with input $input", res.exceptionOrNull())
+ }
+ }
+
+ private fun String.asInputStream() = ByteArrayInputStream(this.encodeToByteArray())
+
+ @Test
+ fun testIterateSeveralItems() = withInputs { ins ->
+ val iter = json.iterateOverStream(ins, StringData.serializer())
+ iter.assertNext(StringData("a"))
+ iter.assertNext(StringData("b"))
+ iter.assertNext(StringData("c"))
+ assertFalse(iter.hasNext())
+ assertFailsWithMessage<SerializationException>("EOF") {
+ iter.next()
+ }
+ }
+
+ @Test
+ fun testDecodeToSequence() = withInputs { ins ->
+ val sequence = json.decodeToSequence(ins, StringData.serializer())
+ assertEquals(inputList, sequence.toList(), "For input $inputStringWsSeparated")
+ assertFailsWith<IllegalStateException> { sequence.toList() } // assert constrained once
+ }
+
+ @Test
+ fun testDecodeAsFlow() = withInputs { ins ->
+ val list = runBlocking {
+ buildList { json.readFromStream<StringData>(ins).toCollection(this) }
+ }
+ assertEquals(inputList, list)
+ }
+
+ @Test
+ fun testItemsSeparatedByWs() {
+ val input = "{\"data\":\"a\"} {\"data\":\"b\"}\n\t{\"data\":\"c\"}"
+ val ins = ByteArrayInputStream(input.encodeToByteArray())
+ assertEquals(inputList, json.decodeToSequence(ins, StringData.serializer()).toList())
+ }
+
+ @Test
+ fun testJsonElement() {
+ val list = listOf<JsonElement>(
+ buildJsonObject { put("foo", "bar") },
+ buildJsonObject { put("foo", "baz") },
+ JsonPrimitive(10),
+ JsonPrimitive("abacaba"),
+ buildJsonObject { put("foo", "qux") }
+ )
+ val inputWs = """${list[0]} ${list[1]} ${list[2]} ${list[3]} ${list[4]}"""
+ val decodedWs = json.decodeToSequence<JsonElement>(inputWs.asInputStream()).toList()
+ assertEquals(list, decodedWs, "Failed whitespace-separated test")
+ val inputArray = """[${list[0]}, ${list[1]},${list[2]} , ${list[3]} ,${list[4]}]"""
+ val decodedArrayWrap = json.decodeToSequence<JsonElement>(inputArray.asInputStream()).toList()
+ assertEquals(list, decodedArrayWrap, "Failed array-wrapped test")
+ }
+
+
+ @Test
+ fun testSealedClasses() {
+ val input = """{"type":"first child","i":1,"j":10} {"type":"first child","i":1,"j":11}"""
+ val iter = json.iterateOverStream(input.asInputStream(), SealedParent.serializer())
+ iter.assertNext(SealedChild(10))
+ iter.assertNext(SealedChild(11))
+ }
+
+ @Test
+ fun testMalformedArray() {
+ val input1 = """[1, 2, 3"""
+ val input2 = """[1, 2, 3]qwert"""
+ val input3 = """[1,2 3]"""
+ withInputs(input1, input2, input3) {
+ assertFailsWith<JsonDecodingException> {
+ json.decodeToSequence(it, Int.serializer()).toList()
+ }
+ }
+ }
+
+ @Test
+ fun testMultilineArrays() {
+ val input = "[1,2,3]\n[4,5,6]\n[7,8,9]"
+ assertFailsWith<JsonDecodingException> {
+ json.decodeToSequence<List<Int>>(input.asInputStream(), DecodeSequenceMode.AUTO_DETECT).toList()
+ }
+ assertFailsWith<JsonDecodingException> {
+ json.decodeToSequence<Int>(input.asInputStream(), DecodeSequenceMode.AUTO_DETECT).toList()
+ }
+ assertFailsWith<JsonDecodingException> { // we do not merge lists
+ json.decodeToSequence<Int>(input.asInputStream(), DecodeSequenceMode.ARRAY_WRAPPED).toList()
+ }
+ val parsed = json.decodeToSequence<List<Int>>(input.asInputStream(), DecodeSequenceMode.WHITESPACE_SEPARATED).toList()
+ val expected = listOf(listOf(1,2,3), listOf(4,5,6), listOf(7,8,9))
+ assertEquals(expected, parsed)
+ }
+
+ @Test
+ fun testStrictArrayCheck() {
+ assertFailsWith<JsonDecodingException> {
+ json.decodeToSequence<StringData>(inputStringWsSeparated.asInputStream(), DecodeSequenceMode.ARRAY_WRAPPED)
+ }
+ }
+
+ @Test
+ fun testPaddedWs() {
+ val paddedWs = " $inputStringWsSeparated "
+ assertEquals(inputList, json.decodeToSequence(paddedWs.asInputStream(), StringData.serializer()).toList())
+ assertEquals(inputList, json.decodeToSequence(paddedWs.asInputStream(), StringData.serializer(), DecodeSequenceMode.WHITESPACE_SEPARATED).toList())
+ }
+
+ @Test
+ fun testPaddedArray() {
+ val paddedWs = " $inputStringWrapped "
+ assertEquals(inputList, json.decodeToSequence(paddedWs.asInputStream(), StringData.serializer()).toList())
+ assertEquals(inputList, json.decodeToSequence(paddedWs.asInputStream(), StringData.serializer(), DecodeSequenceMode.ARRAY_WRAPPED).toList())
+ }
+
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/features/JsonSequencePathTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/features/JsonSequencePathTest.kt
new file mode 100644
index 00000000..287e4438
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/features/JsonSequencePathTest.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.runBlocking
+import kotlinx.serialization.*
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.builtins.serializer
+import kotlinx.serialization.features.sealed.SealedChild
+import kotlinx.serialization.features.sealed.SealedParent
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.internal.JsonDecodingException
+import kotlinx.serialization.test.assertFailsWithMessage
+import org.junit.Test
+import java.io.*
+import kotlin.test.*
+
+class JsonSequencePathTest {
+
+ @Serializable
+ class NestedData(val s: String)
+
+ @Serializable
+ class Data(val data: NestedData)
+
+ @Test
+ fun testFailure() {
+ val source = """{"data":{"s":"value"}}{"data":{"s":42}}{notevenreached}""".toStream()
+ val iterator = Json.decodeToSequence<Data>(source).iterator()
+ iterator.next() // Ignore
+ assertFailsWithMessage<SerializationException>(
+ "Expected quotation mark '\"', but had '2' instead at path: \$.data.s"
+ ) { iterator.next() }
+ }
+
+ private fun String.toStream() = ByteArrayInputStream(encodeToByteArray())
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt
new file mode 100644
index 00000000..5227ca43
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import org.junit.Test
+import java.lang.reflect.*
+import kotlin.reflect.*
+import kotlin.test.*
+
+class SerializerByTypeTest {
+
+ private val json = Json
+
+ @Serializable
+ data class Box<out T>(val a: T)
+
+ @Serializable
+ data class Data(val l: List<String>, val b: Box<Int>)
+
+ @Serializable
+ data class WithCustomDefault(val n: Int) {
+ @Serializer(forClass = WithCustomDefault::class)
+ companion object {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("WithCustomDefault", PrimitiveKind.INT)
+ override fun serialize(encoder: Encoder, value: WithCustomDefault) = encoder.encodeInt(value.n)
+ override fun deserialize(decoder: Decoder) = WithCustomDefault(decoder.decodeInt())
+ }
+ }
+
+ object IntBoxToken : ParameterizedType {
+ override fun getRawType() = Box::class.java
+ override fun getOwnerType() = null
+ override fun getActualTypeArguments(): Array<Type> = arrayOf(Int::class.java)
+ }
+
+ @Serializable
+ object SerializableObject
+
+ @Serializable
+ data class WithNamedCompanion(val a: Int) {
+ companion object Named
+ }
+
+ @Test
+ fun testGenericParameter() {
+ val b = Box(42)
+ assertSerializedWithType(IntBoxToken, """{"a":42}""", b)
+ }
+
+ @Test
+ fun testNestedGenericParameter() {
+ val b = Box(Box(239))
+ assertSerializedWithType(typeTokenOf<Box<Box<Int>>>(), """{"a":{"a":239}}""", b)
+ }
+
+ @Test
+ fun testArray() {
+ val myArr = arrayOf("a", "b", "c")
+ val token = myArr::class.java
+ assertSerializedWithType(token, """["a","b","c"]""", myArr)
+ }
+
+ @Test
+ fun testList() {
+ val myArr = listOf("a", "b", "c")
+ val token = object : ParameterizedType {
+ override fun getRawType(): Type = List::class.java
+ override fun getOwnerType(): Type? = null
+ override fun getActualTypeArguments(): Array<Type> = arrayOf(String::class.java)
+ }
+ assertSerializedWithType(token, """["a","b","c"]""", myArr)
+ }
+
+ @Test
+ fun testListAsCollection() {
+ val myArr: Collection<String> = listOf("a", "b", "c")
+ val token = object : ParameterizedType {
+ override fun getRawType(): Type = Collection::class.java
+ override fun getOwnerType(): Type? = null
+ override fun getActualTypeArguments(): Array<Type> = arrayOf(String::class.java)
+ }
+ assertSerializedWithType(token, """["a","b","c"]""", myArr)
+ }
+
+
+ @Test
+ fun testReifiedArrayResolving() {
+ val myArr = arrayOf("a", "b", "c")
+ val token = typeTokenOf<Array<String>>()
+ assertSerializedWithType(token, """["a","b","c"]""", myArr)
+ }
+
+ @Test
+ fun testPrimitiveArrayResolving() {
+ val myArr = intArrayOf(1, 2, 3)
+ val token = IntArray::class.java
+ val name = serializer(token).descriptor.serialName
+ assertTrue(name.contains("IntArray"))
+ assertSerializedWithType(token, """[1,2,3]""", myArr)
+ }
+
+ @Test
+ fun testReifiedListResolving() {
+ val myList = listOf("a", "b", "c")
+ val token = typeTokenOf<List<String>>()
+ assertSerializedWithType(token, """["a","b","c"]""", myList)
+ }
+
+ @Test
+ fun testReifiedSetResolving() {
+ val mySet = setOf("a", "b", "c", "c")
+ val token = typeTokenOf<Set<String>>()
+ assertSerializedWithType(token, """["a","b","c"]""", mySet)
+ }
+
+ @Test
+ fun testReifiedMapResolving() {
+ val myMap = mapOf("a" to Data(listOf("c"), Box(6)))
+ val token = typeTokenOf<Map<String, Data>>()
+ assertSerializedWithType(token, """{"a":{"l":["c"],"b":{"a":6}}}""",myMap)
+ }
+
+ @Test
+ fun testNestedListResolving() {
+ val myList = listOf(listOf(listOf(1, 2, 3)), listOf())
+ val token = typeTokenOf<List<List<List<Int>>>>()
+ assertSerializedWithType(token, "[[[1,2,3]],[]]", myList)
+ }
+
+ @Test
+ fun testNestedArrayResolving() {
+ val myList = arrayOf(arrayOf(arrayOf(1, 2, 3)), arrayOf())
+ val token = typeTokenOf<Array<Array<Array<Int>>>>()
+ assertSerializedWithType(token, "[[[1,2,3]],[]]", myList)
+ }
+
+ @Test
+ fun testNestedMixedResolving() {
+ val myList = arrayOf(listOf(arrayOf(1, 2, 3)), listOf())
+ val token = typeTokenOf<Array<List<Array<Int>>>>()
+ assertSerializedWithType(token, "[[[1,2,3]],[]]", myList)
+ }
+
+ @Test
+ fun testPair() {
+ val myPair = "42" to 42
+ val token = typeTokenOf<Pair<String, Int>>()
+ assertSerializedWithType(token, """{"first":"42","second":42}""", myPair)
+ }
+
+ @Test
+ fun testTriple() {
+ val myTriple = Triple("1", 2, Box(42))
+ val token = typeTokenOf<Triple<String, Int, Box<Int>>>()
+ assertSerializedWithType(token, """{"first":"1","second":2,"third":{"a":42}}""", myTriple)
+ }
+
+ @Test
+ fun testGenericInHolder() {
+ val b = Data(listOf("a", "b", "c"), Box(42))
+ assertSerializedWithType(Data::class.java,"""{"l":["a","b","c"],"b":{"a":42}}""", b )
+ }
+
+ @Test
+ fun testOverriddenSerializer() {
+ val foo = json.decodeFromString<WithCustomDefault>("9")
+ assertEquals(9, foo.n)
+ }
+
+ @Test
+ fun testNamedCompanion() {
+ val namedCompanion = WithNamedCompanion(1)
+ assertSerializedWithType(WithNamedCompanion::class.java, """{"a":1}""", namedCompanion)
+ }
+
+ @Test
+ fun testPrimitive() {
+ val token = typeTokenOf<Int>()
+ val serial = serializer(token)
+ assertSame(Int.serializer() as KSerializer<*>, serial)
+ }
+
+ @Test
+ fun testObject() {
+ val token = typeTokenOf<SerializableObject>()
+ val serial = serializer(token)
+ assertEquals(SerializableObject.serializer().descriptor, serial.descriptor)
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun <T> assertSerializedWithType(
+ token: Type,
+ expected: String,
+ value: T,
+ ) {
+ val serial = serializer(token) as KSerializer<T>
+ assertEquals(expected, json.encodeToString(serial, value))
+ val serial2 = requireNotNull(serializerOrNull(token)) { "Expected serializer to be found" }
+ assertEquals(expected, json.encodeToString(serial2 as KSerializer<T>, value))
+ }
+
+ @PublishedApi
+ internal open class TypeBase<T>
+
+ public inline fun <reified T> typeTokenOf(): Type {
+ val base = object : TypeBase<T>() {}
+ val superType = base::class.java.genericSuperclass!!
+ return (superType as ParameterizedType).actualTypeArguments.first()!!
+ }
+
+ class IntBox(val i: Int)
+
+ object CustomIntSerializer : KSerializer<IntBox> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("CIS", PrimitiveKind.INT)
+
+ override fun serialize(encoder: Encoder, value: IntBox) {
+ encoder.encodeInt(42)
+ }
+
+ override fun deserialize(decoder: Decoder): IntBox {
+ TODO()
+ }
+ }
+
+ @Test
+ fun testContextualLookup() {
+ val module = SerializersModule { contextual(CustomIntSerializer) }
+ val serializer = module.serializer(typeTokenOf<List<List<IntBox>>>())
+ assertEquals("[[42]]", Json.encodeToString(serializer, listOf(listOf(IntBox(1)))))
+ }
+
+ @Test
+ fun testCompiledWinsOverContextual() {
+ val contextual = object : KSerializer<Int> {
+ override val descriptor: SerialDescriptor = Int.serializer().descriptor
+
+ override fun serialize(encoder: Encoder, value: Int) {
+ fail()
+ }
+
+ override fun deserialize(decoder: Decoder): Int {
+ fail()
+ }
+ }
+ val module = SerializersModule { contextual(contextual) }
+ val serializer = module.serializer(typeTokenOf<List<List<Int>>>())
+ assertEquals("[[1]]", Json.encodeToString(serializer, listOf(listOf<Int>(1))))
+ assertEquals("42", Json.encodeToString(module.serializer(typeTokenOf<Int>()), 42))
+ }
+
+ class NonSerializable
+
+ class NonSerializableBox<T>(val boxed: T)
+
+ @Test
+ fun testLookupFail() {
+ assertNull(serializerOrNull(typeTokenOf<NonSerializable>()))
+ assertNull(serializerOrNull(typeTokenOf<NonSerializableBox<String>>()))
+ assertNull(serializerOrNull(typeTokenOf<Box<NonSerializable>>()))
+
+ assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") {
+ serializer(typeTokenOf<NonSerializable>())
+ }
+
+ assertFailsWithMessage<SerializationException>("for class 'NonSerializableBox'") {
+ serializer(typeTokenOf<NonSerializableBox<String>>())
+ }
+
+ assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") {
+ serializer(typeTokenOf<kotlinx.serialization.Box<NonSerializable>>())
+ }
+
+ assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") {
+ serializer(typeTokenOf<Array<NonSerializable>>())
+ }
+ }
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/json/GsonCompatibilityTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/json/GsonCompatibilityTest.kt
new file mode 100644
index 00000000..99bc23f9
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/json/GsonCompatibilityTest.kt
@@ -0,0 +1,56 @@
+package kotlinx.serialization.json
+
+import com.google.gson.*
+import kotlinx.serialization.*
+import org.junit.Test
+import kotlin.test.*
+
+class GsonCompatibilityTest {
+
+ @Serializable
+ data class Box(val d: Double, val f: Float)
+
+ @Test
+ fun testNaN() {
+ checkCompatibility(Box(Double.NaN, 1.0f))
+ checkCompatibility(Box(1.0, Float.NaN))
+ checkCompatibility(Box(Double.NaN, Float.NaN))
+ }
+
+ @Test
+ fun testInfinity() {
+ checkCompatibility(Box(Double.POSITIVE_INFINITY, 1.0f))
+ checkCompatibility(Box(1.0, Float.POSITIVE_INFINITY))
+ checkCompatibility(Box(Double.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY))
+ }
+
+ @Test
+ fun testNumber() {
+ checkCompatibility(Box(23.9, 23.9f))
+ }
+
+ private fun checkCompatibility(box: Box) {
+ checkCompatibility(box, Gson(), Json)
+ checkCompatibility(box, GsonBuilder().serializeSpecialFloatingPointValues().create(), Json { allowSpecialFloatingPointValues = true })
+ }
+
+ private fun checkCompatibility(box: Box, gson: Gson, json: Json) {
+ val jsonResult = resultOrNull { json.encodeToString(box) }
+ val gsonResult = resultOrNull { gson.toJson(box) }
+ assertEquals(gsonResult, jsonResult)
+
+ if (jsonResult != null && gsonResult != null) {
+ val jsonDeserialized: Box = json.decodeFromString(jsonResult)
+ val gsonDeserialized: Box = gson.fromJson(gsonResult, Box::class.java)
+ assertEquals(gsonDeserialized, jsonDeserialized)
+ }
+ }
+
+ private fun resultOrNull(function: () -> String): String? {
+ return try {
+ function()
+ } catch (t: Throwable) {
+ null
+ }
+ }
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/json/ParallelJsonStressTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/json/ParallelJsonStressTest.kt
new file mode 100644
index 00000000..3901aabe
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/json/ParallelJsonStressTest.kt
@@ -0,0 +1,22 @@
+package kotlinx.serialization.json
+
+import kotlinx.coroutines.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.test.*
+import org.junit.*
+import kotlin.concurrent.*
+import kotlin.random.*
+
+class ParallelJsonStressTest : JsonTestBase() {
+ private val iterations = 1_000_000
+
+ @Test
+ fun testDecodeInParallel() = runBlocking<Unit> {
+ for (i in 1..1000) {
+ launch(Dispatchers.Default) {
+ val value = (1..10000).map { Random.nextDouble() }
+ assertSerializedAndRestored(value, ListSerializer(Double.serializer()))
+ }
+ }
+ }
+}
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/json/SpecConformanceTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/json/SpecConformanceTest.kt
new file mode 100644
index 00000000..96401f72
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/json/SpecConformanceTest.kt
Binary files differ
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/formats/json/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt
new file mode 100644
index 00000000..91aa17f0
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt
@@ -0,0 +1,8 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+
+public actual val currentPlatform: Platform = Platform.JVM
diff --git a/formats/json/jvmTest/src/kotlinx/serialization/test/JsonHelpers.kt b/formats/json/jvmTest/src/kotlinx/serialization/test/JsonHelpers.kt
new file mode 100644
index 00000000..ebb49c35
--- /dev/null
+++ b/formats/json/jvmTest/src/kotlinx/serialization/test/JsonHelpers.kt
@@ -0,0 +1,20 @@
+package kotlinx.serialization.test
+
+import kotlinx.serialization.DeserializationStrategy
+import kotlinx.serialization.SerializationStrategy
+import kotlinx.serialization.json.*
+import java.io.ByteArrayOutputStream
+
+actual fun <T> Json.encodeViaStream(
+ serializer: SerializationStrategy<T>,
+ value: T
+): String {
+ val output = ByteArrayOutputStream()
+ encodeToStream(serializer, value, output)
+ return output.toString()
+}
+
+actual fun <T> Json.decodeViaStream(
+ serializer: DeserializationStrategy<T>,
+ input: String
+): T = decodeFromStream(serializer, input.byteInputStream())
diff --git a/formats/json/nativeMain/src/kotlinx/serialization/json/JsonSchemaCache.kt b/formats/json/nativeMain/src/kotlinx/serialization/json/JsonSchemaCache.kt
new file mode 100644
index 00000000..16e223a0
--- /dev/null
+++ b/formats/json/nativeMain/src/kotlinx/serialization/json/JsonSchemaCache.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.json.internal.*
+import kotlin.native.ref.*
+import kotlin.random.*
+
+/**
+ * This maps emulate thread-locality of DescriptorSchemaCache for Native.
+ *
+ * Custom JSON instances are considered thread-safe (in JVM) and can be frozen and transferred to different workers (in Native).
+ * Therefore, DescriptorSchemaCache should be either a concurrent freeze-aware map or thread local.
+ * Each JSON instance have it's own schemaCache, and it's impossible to use @ThreadLocal on non-global vals.
+ * Thus we make @ThreadLocal this special map: it provides schemaCache for a particular Json instance
+ * and should be used instead of a member `_schemaCache` on Native.
+ *
+ * To avoid memory leaks (when Json instance is no longer in use), WeakReference is used with periodical self-cleaning.
+ */
+@ThreadLocal
+private val jsonToCache: MutableMap<WeakJson, DescriptorSchemaCache> = mutableMapOf()
+
+/**
+ * Because WeakReference itself does not have proper equals/hashCode
+ */
+private class WeakJson(json: Json) {
+ private val ref = WeakReference(json)
+ private val initialHashCode = json.hashCode()
+
+ val isDead: Boolean get() = ref.get() == null
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is WeakJson) return false
+ val thiz = this.ref.get() ?: return false
+ val that = other.ref.get() ?: return false
+ return thiz == that
+ }
+
+ override fun hashCode(): Int = initialHashCode
+}
+
+/**
+ * To maintain O(1) access, we cleanup the table from dead references with 1/size probability
+ */
+private fun cleanUpWeakMap() {
+ val size = jsonToCache.size
+ if (size <= 10) return // 10 is arbitrary small number to ignore polluting
+ // Roll 1/size probability
+ if (Random.nextInt(0, size) == 0) {
+ val iter = jsonToCache.iterator()
+ while (iter.hasNext()) {
+ if (iter.next().key.isDead) iter.remove()
+ }
+ }
+}
+
+/**
+ * Accessor for DescriptorSchemaCache
+ */
+internal actual val Json.schemaCache: DescriptorSchemaCache
+ get() = jsonToCache.getOrPut(WeakJson(this)) { DescriptorSchemaCache() }.also { cleanUpWeakMap() }
diff --git a/formats/json/nativeMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt b/formats/json/nativeMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt
new file mode 100644
index 00000000..1b79e27e
--- /dev/null
+++ b/formats/json/nativeMain/src/kotlinx/serialization/json/internal/JsonStringBuilder.kt
@@ -0,0 +1,28 @@
+package kotlinx.serialization.json.internal
+
+internal actual class JsonStringBuilder actual constructor() {
+ private val sb = StringBuilder(128)
+
+ actual fun append(value: Long) {
+ sb.append(value)
+ }
+
+ actual fun append(ch: Char) {
+ sb.append(ch)
+ }
+
+ actual fun append(string: String) {
+ sb.append(string)
+ }
+
+ actual fun appendQuoted(string: String) {
+ sb.printQuoted(string)
+ }
+
+ actual override fun toString(): String {
+ return sb.toString()
+ }
+
+ actual fun release() {
+ }
+}
diff --git a/formats/json/nativeMain/src/kotlinx/serialization/json/internal/createMapForCache.kt b/formats/json/nativeMain/src/kotlinx/serialization/json/internal/createMapForCache.kt
new file mode 100644
index 00000000..b51ff401
--- /dev/null
+++ b/formats/json/nativeMain/src/kotlinx/serialization/json/internal/createMapForCache.kt
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+/**
+ * Creates a ConcurrentHashMap on JVM and regular HashMap on other platforms.
+ * To make actual use of cache in Kotlin/Native, mark a top-level object with this map
+ * as a @[ThreadLocal].
+ */
+internal actual fun <K, V> createMapForCache(initialCapacity: Int): MutableMap<K, V> = HashMap(initialCapacity)
diff --git a/formats/json/nativeTest/src/kotlinx/serialization/json/MultiWorkerJsonTest.kt b/formats/json/nativeTest/src/kotlinx/serialization/json/MultiWorkerJsonTest.kt
new file mode 100644
index 00000000..2ea063db
--- /dev/null
+++ b/formats/json/nativeTest/src/kotlinx/serialization/json/MultiWorkerJsonTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.*
+import kotlin.native.concurrent.*
+import kotlin.test.*
+
+class MultiWorkerJsonTest {
+ @Serializable
+ data class PlainOne(val one: Int)
+
+ @Serializable
+ data class PlainTwo(val two: Int)
+
+ private fun doTest(json: () -> Json) {
+ val worker = Worker.start()
+ val operation = {
+ for (i in 0..999) {
+ assertEquals(PlainOne(42), json().decodeFromString("""{"one":42,"two":239}"""))
+ }
+ }
+ worker.executeAfter(1000, operation.freeze())
+ for (i in 0..999) {
+ assertEquals(PlainTwo(239), json().decodeFromString("""{"one":42,"two":239}"""))
+ }
+ worker.requestTermination()
+ }
+
+
+ @Test
+ fun testJsonIsFreezeSafe() {
+ val json = Json {
+ isLenient = true
+ ignoreUnknownKeys = true
+ useAlternativeNames = true
+ }
+ // reuse instance
+ doTest { json }
+ }
+
+ @Test
+ fun testJsonInstantiation() {
+ // create new instanceEveryTime
+ doTest {
+ Json {
+ isLenient = true
+ ignoreUnknownKeys = true
+ useAlternativeNames = true
+ }
+ }
+ }
+}
diff --git a/formats/json/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/formats/json/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
new file mode 100644
index 00000000..32806c1f
--- /dev/null
+++ b/formats/json/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+import kotlin.native.concurrent.SharedImmutable
+
+
+@SharedImmutable
+public actual val currentPlatform: Platform = Platform.NATIVE
diff --git a/formats/json/nativeTest/src/kotlinx/serialization/test/JsonHelpers.kt b/formats/json/nativeTest/src/kotlinx/serialization/test/JsonHelpers.kt
new file mode 100644
index 00000000..3f98c43d
--- /dev/null
+++ b/formats/json/nativeTest/src/kotlinx/serialization/test/JsonHelpers.kt
@@ -0,0 +1,19 @@
+package kotlinx.serialization.test
+
+import kotlinx.serialization.DeserializationStrategy
+import kotlinx.serialization.SerializationStrategy
+import kotlinx.serialization.json.Json
+
+actual fun <T> Json.encodeViaStream(
+ serializer: SerializationStrategy<T>,
+ value: T
+): String {
+ TODO("supported on JVM only")
+}
+
+actual fun <T> Json.decodeViaStream(
+ serializer: DeserializationStrategy<T>,
+ input: String
+): T {
+ TODO("supported on JVM only")
+}
diff --git a/formats/properties/api/kotlinx-serialization-properties.api b/formats/properties/api/kotlinx-serialization-properties.api
new file mode 100644
index 00000000..9f15dc06
--- /dev/null
+++ b/formats/properties/api/kotlinx-serialization-properties.api
@@ -0,0 +1,18 @@
+public abstract class kotlinx/serialization/properties/Properties : kotlinx/serialization/SerialFormat {
+ public static final field Default Lkotlinx/serialization/properties/Properties$Default;
+ public synthetic fun <init> (Lkotlinx/serialization/modules/SerializersModule;Ljava/lang/Void;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun decodeFromMap (Lkotlinx/serialization/DeserializationStrategy;Ljava/util/Map;)Ljava/lang/Object;
+ public final fun decodeFromStringMap (Lkotlinx/serialization/DeserializationStrategy;Ljava/util/Map;)Ljava/lang/Object;
+ public final fun encodeToMap (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/util/Map;
+ public final fun encodeToStringMap (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/util/Map;
+ public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public final class kotlinx/serialization/properties/Properties$Default : kotlinx/serialization/properties/Properties {
+}
+
+public final class kotlinx/serialization/properties/PropertiesKt {
+ public static final fun Properties (Lkotlinx/serialization/modules/SerializersModule;)Lkotlinx/serialization/properties/Properties;
+ public static final fun noImpl ()Ljava/lang/Void;
+}
+
diff --git a/formats/properties/build.gradle b/formats/properties/build.gradle
new file mode 100644
index 00000000..dd77ce52
--- /dev/null
+++ b/formats/properties/build.gradle
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+apply plugin: 'kotlin-multiplatform'
+apply plugin: 'kotlinx-serialization'
+apply from: rootProject.file("gradle/native-targets.gradle")
+apply from: rootProject.file("gradle/configure-source-sets.gradle")
+
+
+kotlin {
+
+ sourceSets {
+ commonMain {
+ dependencies {
+ api project(":kotlinx-serialization-core")
+ }
+ }
+
+ jvmTest {
+ dependencies {
+ implementation 'io.kotlintest:kotlintest:2.0.7'
+ implementation 'com.upokecenter:cbor:4.0.0-beta1'
+ implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version"
+ implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
+ implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version"
+ implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:$jackson_version"
+ }
+ }
+ }
+}
+
+Java9Modularity.configureJava9ModuleInfo(project)
diff --git a/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt b/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt
new file mode 100644
index 00000000..9d411ad6
--- /dev/null
+++ b/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("FunctionName", "DeprecatedCallableAddReplaceWith")
+
+package kotlinx.serialization.properties
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.modules.*
+
+/**
+ * Transforms a [Serializable] class' properties into a single flat [Map] consisting of
+ * string keys and primitive type values, and vice versa.
+ *
+ * If the given class has non-primitive property `d` of arbitrary type `D`, `D` values are inserted
+ * into the same map; keys for such values are prefixed with string `d.`:
+ *
+ * ```
+ * @Serializable
+ * class Data(val property1: String)
+ *
+ * @Serializable
+ * class DataHolder(val data: Data, val property2: String)
+ *
+ * val map = Properties.store(DataHolder(Data("value1"), "value2"))
+ * // map contents will be the following:
+ * // property2 = value2
+ * // data.property1 = value1
+ * ```
+ *
+ * If the given class has a [List] property `l`, each value from the list
+ * would be prefixed with `l.N.`, where N is an index for a particular value.
+ * [Map] is treated as a `[key,value,...]` list.
+ *
+ * @param serializersModule A [SerializersModule] which should contain registered serializers
+ * for [Contextual] and [Polymorphic] serialization, if you have any.
+ */
+@ExperimentalSerializationApi
+@Suppress("UNUSED_PARAMETER")
+public sealed class Properties(
+ override val serializersModule: SerializersModule,
+ ctorMarker: Nothing?
+) : SerialFormat {
+
+ private abstract inner class OutMapper<Value : Any> : NamedValueEncoder() {
+ override val serializersModule: SerializersModule = this@Properties.serializersModule
+
+ val map: MutableMap<String, Value> = mutableMapOf()
+
+ protected abstract fun encode(value: Any): Value
+
+ override fun encodeTaggedValue(tag: String, value: Any) {
+ map[tag] = encode(value)
+ }
+
+ override fun encodeTaggedNull(tag: String) {
+ // ignore nulls in output
+ }
+
+ override fun encodeTaggedEnum(tag: String, enumDescriptor: SerialDescriptor, ordinal: Int) {
+ map[tag] = encode(enumDescriptor.getElementName(ordinal))
+ }
+ }
+
+ private inner class OutAnyMapper : OutMapper<Any>() {
+ override fun encode(value: Any): Any = value
+ }
+
+ private inner class OutStringMapper : OutMapper<String>() {
+ override fun encode(value: Any): String = value.toString()
+ }
+
+ private abstract inner class InMapper<Value : Any>(
+ protected val map: Map<String, Value>, descriptor: SerialDescriptor
+ ) : NamedValueDecoder() {
+ override val serializersModule: SerializersModule = this@Properties.serializersModule
+
+ private var currentIndex = 0
+ private val isCollection = descriptor.kind == StructureKind.LIST || descriptor.kind == StructureKind.MAP
+ private val size = if (isCollection) Int.MAX_VALUE else descriptor.elementsCount
+
+ protected abstract fun structure(descriptor: SerialDescriptor): InMapper<Value>
+
+ final override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ return structure(descriptor).also { copyTagsTo(it) }
+ }
+
+ final override fun decodeTaggedValue(tag: String): Value {
+ return map.getValue(tag)
+ }
+
+ final override fun decodeTaggedEnum(tag: String, enumDescriptor: SerialDescriptor): Int {
+ return when (val taggedValue = map.getValue(tag)) {
+ is Int -> taggedValue
+ is String -> enumDescriptor.getElementIndex(taggedValue)
+ .also { if (it == CompositeDecoder.UNKNOWN_NAME) throw SerializationException("Enum '${enumDescriptor.serialName}' does not contain element with name '$taggedValue'") }
+ else -> throw SerializationException("Value of enum entry '$tag' is neither an Int nor a String")
+ }
+ }
+
+ final override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ while (currentIndex < size) {
+ val name = descriptor.getTag(currentIndex++)
+ if (map.keys.any {
+ it.startsWith(name) && (it.length == name.length || it[name.length] == '.')
+ }) return currentIndex - 1
+ if (isCollection) {
+ // if map does not contain key we look for, then indices in collection have ended
+ break
+ }
+ }
+ return CompositeDecoder.DECODE_DONE
+ }
+ }
+
+ private inner class InAnyMapper(
+ map: Map<String, Any>, descriptor: SerialDescriptor
+ ) : InMapper<Any>(map, descriptor) {
+ override fun structure(descriptor: SerialDescriptor): InAnyMapper =
+ InAnyMapper(map, descriptor)
+ }
+
+ private inner class InStringMapper(
+ map: Map<String, String>, descriptor: SerialDescriptor
+ ) : InMapper<String>(map, descriptor) {
+ override fun structure(descriptor: SerialDescriptor): InStringMapper =
+ InStringMapper(map, descriptor)
+
+ override fun decodeTaggedBoolean(tag: String): Boolean = decodeTaggedValue(tag).toBoolean()
+ override fun decodeTaggedByte(tag: String): Byte = decodeTaggedValue(tag).toByte()
+ override fun decodeTaggedShort(tag: String): Short = decodeTaggedValue(tag).toShort()
+ override fun decodeTaggedInt(tag: String): Int = decodeTaggedValue(tag).toInt()
+ override fun decodeTaggedLong(tag: String): Long = decodeTaggedValue(tag).toLong()
+ override fun decodeTaggedFloat(tag: String): Float = decodeTaggedValue(tag).toFloat()
+ override fun decodeTaggedDouble(tag: String): Double = decodeTaggedValue(tag).toDouble()
+ override fun decodeTaggedChar(tag: String): Char = decodeTaggedValue(tag).single()
+ }
+
+ /**
+ * Encodes properties from the given [value] to a map using the given [serializer].
+ * `null` values are omitted from the output.
+ */
+ @ExperimentalSerializationApi
+ public fun <T> encodeToMap(serializer: SerializationStrategy<T>, value: T): Map<String, Any> {
+ val m = OutAnyMapper()
+ m.encodeSerializableValue(serializer, value)
+ return m.map
+ }
+
+ /**
+ * Encodes properties from the given [value] to a map using the given [serializer].
+ * Converts all primitive types to [String] using [toString] method.
+ * `null` values are omitted from the output.
+ */
+ @ExperimentalSerializationApi
+ public fun <T> encodeToStringMap(serializer: SerializationStrategy<T>, value: T): Map<String, String> {
+ val m = OutStringMapper()
+ m.encodeSerializableValue(serializer, value)
+ return m.map
+ }
+
+ /**
+ * Decodes properties from the given [map] to a value of type [T] using the given [deserializer].
+ * [T] may contain properties of nullable types; they will be filled by non-null values from the [map], if present.
+ */
+ @ExperimentalSerializationApi
+ public fun <T> decodeFromMap(deserializer: DeserializationStrategy<T>, map: Map<String, Any>): T {
+ val m = InAnyMapper(map, deserializer.descriptor)
+ return m.decodeSerializableValue(deserializer)
+ }
+
+ /**
+ * Decodes properties from the given [map] to a value of type [T] using the given [deserializer].
+ * [String] values are converted to respective primitive types using default conversion methods.
+ * [T] may contain properties of nullable types; they will be filled by non-null values from the [map], if present.
+ */
+ @ExperimentalSerializationApi
+ public fun <T> decodeFromStringMap(deserializer: DeserializationStrategy<T>, map: Map<String, String>): T {
+ val m = InStringMapper(map, deserializer.descriptor)
+ return m.decodeSerializableValue(deserializer)
+ }
+
+ /**
+ * A [Properties] instance that can be used as default and does not have any [SerializersModule] installed.
+ */
+ @ExperimentalSerializationApi
+ public companion object Default : Properties(EmptySerializersModule, null)
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+private class PropertiesImpl(serializersModule: SerializersModule) : Properties(serializersModule, null)
+
+/**
+ * Creates an instance of [Properties] with a given [module].
+ */
+@ExperimentalSerializationApi
+public fun Properties(module: SerializersModule): Properties = PropertiesImpl(module)
+
+/**
+ * Encodes properties from given [value] to a map using serializer for reified type [T] and returns this map.
+ * `null` values are omitted from the output.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Properties.encodeToMap(value: T): Map<String, Any> =
+ encodeToMap(serializersModule.serializer(), value)
+
+/**
+ * Encodes properties from given [value] to a map using serializer for reified type [T] and returns this map.
+ * Converts all primitive types to [String] using [toString] method.
+ * `null` values are omitted from the output.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Properties.encodeToStringMap(value: T): Map<String, String> =
+ encodeToStringMap(serializersModule.serializer(), value)
+
+/**
+ * Decodes properties from given [map], assigns them to an object using serializer for reified type [T] and returns this object.
+ * [T] may contain properties of nullable types; they will be filled by non-null values from the [map], if present.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Properties.decodeFromMap(map: Map<String, Any>): T =
+ decodeFromMap(serializersModule.serializer(), map)
+
+/**
+ * Decodes properties from given [map], assigns them to an object using serializer for reified type [T] and returns this object.
+ * [String] values are converted to respective primitive types using default conversion methods.
+ * [T] may contain properties of nullable types; they will be filled by non-null values from the [map], if present.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Properties.decodeFromStringMap(map: Map<String, String>): T =
+ decodeFromStringMap(serializersModule.serializer(), map)
+
+// Migrations below
+
+@PublishedApi
+internal fun noImpl(): Nothing = throw UnsupportedOperationException("Not implemented, should not be called")
diff --git a/formats/properties/commonTest/src/kotlinx/serialization/properties/PropertiesTest.kt b/formats/properties/commonTest/src/kotlinx/serialization/properties/PropertiesTest.kt
new file mode 100644
index 00000000..09af673a
--- /dev/null
+++ b/formats/properties/commonTest/src/kotlinx/serialization/properties/PropertiesTest.kt
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+
+package kotlinx.serialization.properties
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlin.test.*
+
+class PropertiesTest {
+
+ @Serializable
+ data class Data(val list: List<String>, val property: String)
+
+ @Serializable
+ data class Recursive(val data: Data, val property: String)
+
+ @Serializable
+ data class NullableData(val property: String, val nullable: String? = null, val nullable2: String? = null)
+
+ @Serializable
+ data class Category(var name: String? = null, var subCategory: SubCategory? = null)
+
+ @Serializable
+ data class SubCategory(var name: String? = null, var option: String? = null)
+
+ @Serializable
+ data class DataWithMap(val map: Map<String, Int> = mapOf())
+
+ @Serializable
+ data class MultiType(
+ val first: Int,
+ val second: String,
+ val unit: Unit = Unit,
+ val last: Boolean = true
+ )
+
+ @Serializable
+ data class TestWithSize(
+ val p: String? = null,
+ val size: String? = null
+ )
+
+ @Serializable
+ data class EnumData(val data: TestEnum)
+
+ @Serializable
+ data class NullableEnumData(val data0: TestEnum?, val data1: TestEnum?)
+
+ @Serializable
+ data class SharedPrefixNames(
+ val first: String = "100",
+ val firstSecond: String = "100"
+ )
+
+ @Serializable
+ enum class TestEnum { ZERO, ONE }
+
+ private inline fun <reified T : Any> assertMappedAndRestored(
+ expectedMap: Map<String, Any>,
+ obj: T,
+ serializer: KSerializer<T>
+ ) {
+ val map = Properties.encodeToMap(serializer, obj)
+ assertEquals(expectedMap, map)
+ val unmap = Properties.decodeFromMap(serializer, map)
+ assertEquals(obj, unmap)
+
+ val stringMap = Properties.encodeToStringMap(serializer, obj)
+ val expectedStringMap = expectedMap.mapValues { it.value.toString() }
+ assertEquals(expectedStringMap, stringMap)
+ val stringUnmap = Properties.decodeFromStringMap(serializer, stringMap)
+ assertEquals(obj, stringUnmap)
+ }
+
+ private inline fun <reified T : Any> assertMappedNullableAndRestored(
+ expectedMap: Map<String, Any?>,
+ obj: T,
+ serializer: KSerializer<T>
+ ) {
+ val map = Properties.encodeToMap(serializer, obj)
+ assertEquals(expectedMap, map)
+ val unmap = Properties.decodeFromMap(serializer, map)
+ assertEquals(obj, unmap)
+
+ val stringMap = Properties.encodeToStringMap(serializer, obj)
+ val expectedStringMap = expectedMap.mapValues { it.value.toString() }
+ assertEquals(expectedStringMap, stringMap)
+ val stringUnmap = Properties.decodeFromStringMap(serializer, stringMap)
+ assertEquals(obj, stringUnmap)
+ }
+
+ @Test
+ fun testMultipleTypes() {
+ val data = MultiType(1, "2")
+ assertMappedAndRestored(
+ mapOf("first" to 1, "second" to "2", "last" to true),
+ data,
+ MultiType.serializer()
+ )
+ }
+
+ @Test
+ fun testUnitIsEmptyMap() {
+ assertEquals(emptyMap(), Properties.encodeToMap(Unit.serializer(), Unit))
+ }
+
+ @Test
+ fun testList() {
+ val data = Data(listOf("element1"), "property")
+ assertMappedAndRestored(
+ mapOf(
+ "list.0" to "element1",
+ "property" to "property"
+ ),
+ data,
+ Data.serializer()
+ )
+ }
+
+ @Test
+ fun testNestedStructure() {
+ val recursive = Recursive(
+ Data(
+ listOf("l1"),
+ "property"
+ ), "string"
+ )
+ val mapOf =
+ mapOf("data.list.0" to "l1", "data.property" to "property", "property" to "string")
+ assertMappedAndRestored(mapOf, recursive, Recursive.serializer())
+ }
+
+ @Test
+ fun testNullableProperties() {
+ val data = NullableData("property", null, null)
+ val expectedMap = mapOf("property" to "property")
+ assertMappedNullableAndRestored(expectedMap, data, NullableData.serializer())
+ }
+
+ @Test
+ fun testNestedNull() {
+ val category = Category(name = "Name")
+ val expectedMap = mapOf("name" to "Name")
+ assertMappedNullableAndRestored(expectedMap, category, Category.serializer())
+ }
+
+ @Test
+ fun testLoadOptionalProps() {
+ val map: Map<String, Any> = mapOf("name" to "Name")
+ val restored = Properties.decodeFromMap<Category>(Category.serializer(), map)
+ assertEquals(Category("Name"), restored)
+ }
+
+ @Test
+ fun testLoadOptionalNestedProps() {
+ val map: Map<String, Any> = mapOf("name" to "Name", "subCategory.name" to "SubName")
+ val restored = Properties.decodeFromMap<Category>(Category.serializer(), map)
+ assertEquals(Category("Name", SubCategory("SubName")), restored)
+ }
+
+ @Test
+ fun testOmitsNullAndCanLoadBack() {
+ val category = Category(name = "Name")
+ val expectedMap = mapOf("name" to "Name")
+ assertMappedAndRestored(expectedMap, category, Category.serializer())
+ }
+
+ @Test
+ fun testThrowsOnIncorrectMaps() {
+ val map: Map<String, Any> = mapOf("name" to "Name")
+ assertFailsWith<SerializationException> {
+ Properties.decodeFromMap(Data.serializer(), map)
+ }
+ }
+
+ @Test
+ fun testNestedMap() {
+ val map0 = DataWithMap(mapOf())
+ val map1 = DataWithMap(mapOf("one" to 1))
+ val map2 = DataWithMap(mapOf("one" to 1, "two" to 2))
+
+ fun doTest(testData: DataWithMap) {
+ val map = Properties.encodeToMap(
+ DataWithMap.serializer(),
+ testData
+ )
+ val d2 = Properties.decodeFromMap(
+ DataWithMap.serializer(),
+ map
+ )
+ assertEquals(testData, d2)
+ }
+
+ doTest(map0)
+ doTest(map1)
+ doTest(map2)
+ }
+
+ @Test
+ fun testEnumString() {
+ val map = mapOf("data" to "ZERO")
+ val loaded = Properties.decodeFromMap(EnumData.serializer(), map)
+ assertEquals(EnumData(TestEnum.ZERO), loaded)
+ }
+
+
+ @Test
+ fun testEnumInteger() {
+ val map = mapOf("data" to 0)
+ val loaded = Properties.decodeFromMap(EnumData.serializer(), map)
+ assertEquals(EnumData(TestEnum.ZERO), loaded)
+ }
+
+ @Test
+ fun testCanReadSizeProperty() {
+ assertMappedAndRestored(mapOf("p" to "a", "size" to "b"), TestWithSize("a", "b"), TestWithSize.serializer())
+ }
+
+ @Test
+ fun testSharedPrefixNames() {
+ val map: Map<String, Any> = mapOf("firstSecond" to "42")
+ val restored = Properties.decodeFromMap(SharedPrefixNames.serializer(), map)
+ assertEquals(SharedPrefixNames("100", "42"), restored)
+ }
+
+ @Test
+ fun testEnumElementNotFound() {
+ val wrongElementName = "wrong"
+ val expectedMessage =
+ "Enum '${TestEnum.serializer().descriptor.serialName}' does not contain element with name '${wrongElementName}'"
+
+ val exception = assertFailsWith(SerializationException::class) {
+ Properties.decodeFromStringMap(EnumData.serializer(), mapOf("data" to wrongElementName))
+ }
+ assertEquals(expectedMessage, exception.message)
+ }
+}
diff --git a/formats/properties/jvmMainModule/src/module-info.java b/formats/properties/jvmMainModule/src/module-info.java
new file mode 100644
index 00000000..7be4c18d
--- /dev/null
+++ b/formats/properties/jvmMainModule/src/module-info.java
@@ -0,0 +1,6 @@
+module kotlinx.serialization.properties {
+ requires transitive kotlin.stdlib;
+ requires transitive kotlinx.serialization.core;
+
+ exports kotlinx.serialization.properties;
+}
diff --git a/formats/protobuf/api/kotlinx-serialization-protobuf.api b/formats/protobuf/api/kotlinx-serialization-protobuf.api
new file mode 100644
index 00000000..65093b2c
--- /dev/null
+++ b/formats/protobuf/api/kotlinx-serialization-protobuf.api
@@ -0,0 +1,64 @@
+public abstract class kotlinx/serialization/protobuf/ProtoBuf : kotlinx/serialization/BinaryFormat {
+ public static final field Default Lkotlinx/serialization/protobuf/ProtoBuf$Default;
+ public synthetic fun <init> (ZLkotlinx/serialization/modules/SerializersModule;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun decodeFromByteArray (Lkotlinx/serialization/DeserializationStrategy;[B)Ljava/lang/Object;
+ public fun encodeToByteArray (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)[B
+ public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public final class kotlinx/serialization/protobuf/ProtoBuf$Default : kotlinx/serialization/protobuf/ProtoBuf {
+}
+
+public final class kotlinx/serialization/protobuf/ProtoBufBuilder {
+ public final fun getEncodeDefaults ()Z
+ public final fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+ public final fun setEncodeDefaults (Z)V
+ public final fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V
+}
+
+public final class kotlinx/serialization/protobuf/ProtoBufKt {
+ public static final fun ProtoBuf (Lkotlinx/serialization/protobuf/ProtoBuf;Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/protobuf/ProtoBuf;
+ public static synthetic fun ProtoBuf$default (Lkotlinx/serialization/protobuf/ProtoBuf;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/serialization/protobuf/ProtoBuf;
+}
+
+public final class kotlinx/serialization/protobuf/ProtoIntegerType : java/lang/Enum {
+ public static final field DEFAULT Lkotlinx/serialization/protobuf/ProtoIntegerType;
+ public static final field FIXED Lkotlinx/serialization/protobuf/ProtoIntegerType;
+ public static final field SIGNED Lkotlinx/serialization/protobuf/ProtoIntegerType;
+ public static fun valueOf (Ljava/lang/String;)Lkotlinx/serialization/protobuf/ProtoIntegerType;
+ public static fun values ()[Lkotlinx/serialization/protobuf/ProtoIntegerType;
+}
+
+public abstract interface annotation class kotlinx/serialization/protobuf/ProtoNumber : java/lang/annotation/Annotation {
+ public abstract fun number ()I
+}
+
+public final class kotlinx/serialization/protobuf/ProtoNumber$Impl : kotlinx/serialization/protobuf/ProtoNumber {
+ public fun <init> (I)V
+ public final synthetic fun number ()I
+}
+
+public abstract interface annotation class kotlinx/serialization/protobuf/ProtoPacked : java/lang/annotation/Annotation {
+}
+
+public final class kotlinx/serialization/protobuf/ProtoPacked$Impl : kotlinx/serialization/protobuf/ProtoPacked {
+ public fun <init> ()V
+}
+
+public abstract interface annotation class kotlinx/serialization/protobuf/ProtoType : java/lang/annotation/Annotation {
+ public abstract fun type ()Lkotlinx/serialization/protobuf/ProtoIntegerType;
+}
+
+public final class kotlinx/serialization/protobuf/ProtoType$Impl : kotlinx/serialization/protobuf/ProtoType {
+ public fun <init> (Lkotlinx/serialization/protobuf/ProtoIntegerType;)V
+ public final synthetic fun type ()Lkotlinx/serialization/protobuf/ProtoIntegerType;
+}
+
+public final class kotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator {
+ public static final field INSTANCE Lkotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator;
+ public final fun generateSchemaText (Ljava/util/List;Ljava/lang/String;Ljava/util/Map;)Ljava/lang/String;
+ public final fun generateSchemaText (Lkotlinx/serialization/descriptors/SerialDescriptor;Ljava/lang/String;Ljava/util/Map;)Ljava/lang/String;
+ public static synthetic fun generateSchemaText$default (Lkotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator;Ljava/util/List;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Ljava/lang/String;
+ public static synthetic fun generateSchemaText$default (Lkotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator;Lkotlinx/serialization/descriptors/SerialDescriptor;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Ljava/lang/String;
+}
+
diff --git a/formats/protobuf/build.gradle b/formats/protobuf/build.gradle
new file mode 100644
index 00000000..c2183b28
--- /dev/null
+++ b/formats/protobuf/build.gradle
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+apply plugin: 'java' // Needed for protobuf plugin only
+apply plugin: 'kotlin-multiplatform'
+apply plugin: 'kotlinx-serialization'
+apply plugin: 'com.google.protobuf'
+apply from: rootProject.file("gradle/native-targets.gradle")
+apply from: rootProject.file("gradle/configure-source-sets.gradle")
+
+protobuf {
+ protoc {
+ // Download from repositories
+ artifact = 'com.google.protobuf:protoc:3.17.3'
+ }
+}
+
+clean {
+ delete protobuf.generatedFilesBaseDir
+}
+
+kotlin {
+
+ sourceSets {
+ commonMain {
+ dependencies {
+ api project(":kotlinx-serialization-core")
+ }
+ }
+
+ jvmTest {
+ kotlin.srcDirs += file("${protobuf.generatedFilesBaseDir}/test/java")
+ dependencies {
+ implementation 'com.google.protobuf:protobuf-java:3.17.3'
+ implementation 'io.kotlintest:kotlintest:2.0.7'
+ }
+ }
+ }
+}
+
+sourceSets.test.proto {
+ srcDirs = ['testProto', 'jvmTest/resources/common']
+}
+
+compileTestKotlinJvm {
+ dependsOn 'generateTestProto'
+}
+
+Java9Modularity.configureJava9ModuleInfo(project)
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt
new file mode 100644
index 00000000..8f447ef3
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.protobuf.internal.*
+import kotlin.js.*
+
+/**
+ * Implements [encoding][encodeToByteArray] and [decoding][decodeFromByteArray] classes to/from bytes
+ * using [Proto2][https://developers.google.com/protocol-buffers/docs/proto] specification.
+ * It is typically used by constructing an application-specific instance, with configured specific behaviour
+ * and, if necessary, registered custom serializers (in [SerializersModule] provided by [serializersModule] constructor parameter).
+ *
+ * ### Correspondence between Protobuf message definitions and Kotlin classes
+ * Given a ProtoBuf definition with one required field, one optional field and one optional field with a custom default
+ * value:
+ * ```
+ * message MyMessage {
+ * required int32 first = 1;
+ * optional int32 second = 2;
+ * optional int32 third = 3 [default = 42];
+ * }
+ * ```
+ *
+ * The corresponding [Serializable] class should match the ProtoBuf definition and should use the same default values:
+ * ```
+ * @Serializable
+ * data class MyMessage(val first: Int, val second: Int = 0, val third: Int = 42)
+ * ```
+ *
+ * By default, protobuf fields ids are being assigned to Kotlin properties in incremental order, i.e.
+ * the first property in the class has id 1, the second has id 2, and so forth.
+ * If you need a more stable order (e.g. to avoid breaking changes when reordering properties),
+ * provide custom ids using [ProtoNumber] annotation.
+ *
+ * By default, all integer numbers are encoded using [varint][https://developers.google.com/protocol-buffers/docs/encoding#varints]
+ * encoding. This behaviour can be changed via [ProtoType] annotation.
+ *
+ * ### Known caveats and limitations
+ * Lists are represented as repeated fields. Because format spec says that if the list is empty,
+ * there are no elements in the stream with such tag, you must explicitly mark any
+ * field of list type with default = emptyList(). Same for maps.
+ * There's no special support for `oneof` protobuf fields. However, this implementation
+ * supports standard kotlinx.serialization's polymorphic and sealed serializers,
+ * using their default form (message of serialName: string and other embedded message with actual content).
+ *
+ * ### Proto3 support
+ * This implementation does not support repeated packed fields, so you won't be able to deserialize
+ * Proto3 lists. However, other messages could be decoded. You have to remember that since fields in Proto3
+ * messages by default are implicitly optional,
+ * corresponding Kotlin properties have to be nullable with default value `null`.
+ *
+ * ### Usage example
+ * ```
+ * // Serialize to ProtoBuf bytes. Default values are omitted.
+ * val encoded = ProtoBuf.encodeToByteArray(MyMessage(15)) // [0x08, 0x0f]
+ *
+ * // Deserialize ProtoBuf bytes will use default values of the MyMessage class
+ * val decoded = ProtoBuf.decodeFromByteArray<MyMessage>(encoded) // MyMessage(first=15, second=0, third=42)
+ *
+ * // Serialize to ProtoBuf hex string with all values
+ * val encoded2 = ProtoBuf { encodeDefaults = true }.encodeToHexString(MyMessage(15)) // "080f1000182a"
+ *
+ * // Deserialize from ProtoBuf hex string
+ * val decoded2 = ProtoBuf.decodeFromHexString<MyMessage>(encoded2) // MyMessage(first=15, second=0, third=42)
+ * ```
+ *
+ * ### Check existence of optional fields
+ * Null values can be used as the default value for optional fields to implement more complex use-cases that rely on
+ * checking if a field was set or not.
+ *
+ * ```
+ * @Serializable
+ * data class MyMessage(val first: Int, private val _second: Int? = null, private val _third: Int? = null) {
+ *
+ * val second: Int
+ * get() = _second ?: 0
+ *
+ * val third: Int
+ * get() = _third ?: 42
+ *
+ * fun hasSecond() = _second != null
+ *
+ * fun hasThird() = _third != null
+ * }
+ *
+ * // Serialize to ProtoBuf bytes, removing all default (null) values
+ * val encoded = ProtoBuf.encodeToByteArray(MyMessage(15)) // [0x08, 0x0f]
+ *
+ * // Deserialize ProtoBuf bytes
+ * val decoded = ProtoBuf.decodeFromByteArray<MyMessage>(encoded) // MyMessage(first = 15, _second = null, _third = null)
+ * decoded.hasSecond() // false
+ * decoded.second // 0
+ * decoded.hasThird() // false
+ * decoded.third // 42
+ *
+ * // Serialize to ProtoBuf bytes
+ * val encoded2 = ProtoBuf.encodeToByteArray(MyMessage(15, 0, 0)) // [0x08, 0x0f, 0x10, 0x00, 0x18, 0x00]
+ *
+ * // Deserialize ProtoBuf bytes
+ * val decoded2 = ProtoBuf.decodeFromByteArray<MyMessage>(encoded2) // MyMessage(first=15, _second=0, _third=0)
+ * decoded.hasSecond() // true
+ * decoded.second // 0
+ * decoded.hasThird() // true
+ * decoded.third // 0
+ * ```
+ *
+ * @param encodeDefaults specifies whether default values are encoded.
+ * False by default; meaning that properties with values equal to defaults will be elided.
+ * @param serializersModule application-specific [SerializersModule] to provide custom serializers.
+ */
+@ExperimentalSerializationApi
+public sealed class ProtoBuf(
+ internal val encodeDefaults: Boolean,
+ override val serializersModule: SerializersModule
+) : BinaryFormat {
+
+ /**
+ * The default instance of [ProtoBuf].
+ */
+ public companion object Default : ProtoBuf(false, EmptySerializersModule)
+
+ override fun <T> encodeToByteArray(serializer: SerializationStrategy<T>, value: T): ByteArray {
+ val output = ByteArrayOutput()
+ val encoder = ProtobufEncoder(this, ProtobufWriter(output), serializer.descriptor)
+ encoder.encodeSerializableValue(serializer, value)
+ return output.toByteArray()
+ }
+
+ override fun <T> decodeFromByteArray(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T {
+ val input = ByteArrayInput(bytes)
+ val decoder = ProtobufDecoder(this, ProtobufReader(input), deserializer.descriptor)
+ return decoder.decodeSerializableValue(deserializer)
+ }
+}
+
+/**
+ * Creates an instance of [ProtoBuf] configured from the optionally given [ProtoBuf instance][from]
+ * and adjusted with [builderAction].
+ */
+@ExperimentalSerializationApi
+public fun ProtoBuf(from: ProtoBuf = ProtoBuf, builderAction: ProtoBufBuilder.() -> Unit): ProtoBuf {
+ val b = ProtoBufBuilder(from)
+ b.builderAction()
+ return ProtoBufImpl(b.encodeDefaults, b.serializersModule)
+}
+
+/**
+ * Builder of the [ProtoBuf] instance provided by `ProtoBuf` factory function.
+ */
+@ExperimentalSerializationApi
+public class ProtoBufBuilder internal constructor(proto: ProtoBuf) {
+
+ /**
+ * Specifies whether default values of Kotlin properties should be encoded.
+ */
+ public var encodeDefaults: Boolean = proto.encodeDefaults
+
+ /**
+ * Module with contextual and polymorphic serializers to be used in the resulting [ProtoBuf] instance.
+ */
+ public var serializersModule: SerializersModule = proto.serializersModule
+}
+
+@ExperimentalSerializationApi
+private class ProtoBufImpl(encodeDefaults: Boolean, serializersModule: SerializersModule) :
+ ProtoBuf(encodeDefaults, serializersModule)
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt
new file mode 100644
index 00000000..3b62d4dc
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+
+/**
+ * Specifies protobuf field number (a unique number for a field in the protobuf message)
+ * assigned to a Kotlin property.
+ *
+ * See [https://developers.google.com/protocol-buffers/docs/proto#assigning-field-numbers]
+ */
+@SerialInfo
+@Target(AnnotationTarget.PROPERTY)
+@ExperimentalSerializationApi
+public annotation class ProtoNumber(public val number: Int)
+
+/**
+ * Represents a number format in protobuf encoding.
+ *
+ * [DEFAULT] is default varint encoding (intXX),
+ * [SIGNED] is signed ZigZag representation (sintXX), and
+ * [FIXED] is fixedXX type.
+ * uintXX and sfixedXX are not supported yet.
+ *
+ * See [https://developers.google.com/protocol-buffers/docs/proto#scalar]
+ * @see ProtoType
+ */
+@Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING")
+@ExperimentalSerializationApi
+public enum class ProtoIntegerType(internal val signature: Long) {
+ DEFAULT(0L shl 33),
+ SIGNED(1L shl 33),
+ FIXED(2L shl 33);
+}
+
+/**
+ * Instructs to use a particular [ProtoIntegerType] for a property of integer number type.
+ * Affect [Byte], [Short], [Int], [Long] and [Char] properties and does not affect others.
+ */
+@SerialInfo
+@Target(AnnotationTarget.PROPERTY)
+@ExperimentalSerializationApi
+public annotation class ProtoType(public val type: ProtoIntegerType)
+
+
+/**
+ * Instructs that a particular collection should be written as [packed array](https://developers.google.com/protocol-buffers/docs/encoding#packed)
+ */
+@SerialInfo
+@Target(AnnotationTarget.PROPERTY)
+@ExperimentalSerializationApi
+public annotation class ProtoPacked
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt
new file mode 100644
index 00000000..59533db0
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.protobuf.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.protobuf.*
+
+internal typealias ProtoDesc = Long
+internal const val VARINT = 0
+internal const val i64 = 1
+internal const val SIZE_DELIMITED = 2
+internal const val i32 = 5
+
+private const val INTTYPEMASK = (Int.MAX_VALUE.toLong() shr 1) shl 33
+private const val PACKEDMASK = 1L shl 32
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun ProtoDesc(protoId: Int, type: ProtoIntegerType, packed: Boolean): ProtoDesc {
+ val packedBits = if (packed) 1L shl 32 else 0L
+ val signature = type.signature or packedBits
+ return signature or protoId.toLong()
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun ProtoDesc(protoId: Int, type: ProtoIntegerType): ProtoDesc {
+ return type.signature or protoId.toLong()
+}
+
+internal inline val ProtoDesc.protoId: Int get() = (this and Int.MAX_VALUE.toLong()).toInt()
+
+internal val ProtoDesc.integerType: ProtoIntegerType
+ get() = when(this and INTTYPEMASK) {
+ ProtoIntegerType.DEFAULT.signature -> ProtoIntegerType.DEFAULT
+ ProtoIntegerType.SIGNED.signature -> ProtoIntegerType.SIGNED
+ else -> ProtoIntegerType.FIXED
+}
+
+internal val SerialDescriptor.isPackable: Boolean
+ @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class)
+ get() = when (kind) {
+ PrimitiveKind.STRING,
+ !is PrimitiveKind -> false
+ else -> true
+ }
+
+internal val ProtoDesc.isPacked: Boolean
+ get() = (this and PACKEDMASK) != 0L
+
+internal fun SerialDescriptor.extractParameters(index: Int): ProtoDesc {
+ val annotations = getElementAnnotations(index)
+ var protoId: Int = index + 1
+ var format: ProtoIntegerType = ProtoIntegerType.DEFAULT
+ var protoPacked = false
+
+ for (i in annotations.indices) { // Allocation-friendly loop
+ val annotation = annotations[i]
+ if (annotation is ProtoNumber) {
+ protoId = annotation.number
+ } else if (annotation is ProtoType) {
+ format = annotation.type
+ } else if (annotation is ProtoPacked) {
+ protoPacked = true
+ }
+ }
+ return ProtoDesc(protoId, format, protoPacked)
+}
+
+internal fun extractProtoId(descriptor: SerialDescriptor, index: Int, zeroBasedDefault: Boolean): Int {
+ val annotations = descriptor.getElementAnnotations(index)
+ for (i in annotations.indices) { // Allocation-friendly loop
+ val annotation = annotations[i]
+ if (annotation is ProtoNumber) {
+ return annotation.number
+ }
+ }
+ return if (zeroBasedDefault) index else index + 1
+}
+
+internal class ProtobufDecodingException(message: String) : SerializationException(message)
+
+internal expect fun Int.reverseBytes(): Int
+internal expect fun Long.reverseBytes(): Long
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/PackedArrayDecoder.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/PackedArrayDecoder.kt
new file mode 100644
index 00000000..b17d5119
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/PackedArrayDecoder.kt
@@ -0,0 +1,32 @@
+package kotlinx.serialization.protobuf.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.protobuf.*
+
+@OptIn(ExperimentalSerializationApi::class)
+internal class PackedArrayDecoder(
+ proto: ProtoBuf,
+ reader: ProtobufReader,
+ descriptor: SerialDescriptor,
+) : ProtobufDecoder(proto, reader, descriptor) {
+ private var nextIndex: Int = 0
+
+ // Tags are omitted in the packed array format
+ override fun SerialDescriptor.getTag(index: Int): ProtoDesc = MISSING_TAG
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ throw SerializationException("Packing only supports primitive number types. The input type however was a struct: $descriptor")
+ }
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ // We need eof here as there is no tag to read in packed form.
+ if (reader.eof) return CompositeDecoder.DECODE_DONE
+ return nextIndex++
+ }
+
+ override fun decodeTaggedString(tag: ProtoDesc): String {
+ throw SerializationException("Packing only supports primitive number types. The actual reading is for string.")
+ }
+} \ No newline at end of file
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/PackedArrayEncoder.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/PackedArrayEncoder.kt
new file mode 100644
index 00000000..812ca307
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/PackedArrayEncoder.kt
@@ -0,0 +1,31 @@
+package kotlinx.serialization.protobuf.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.protobuf.*
+
+@OptIn(ExperimentalSerializationApi::class)
+internal class PackedArrayEncoder(
+ proto: ProtoBuf,
+ writer: ProtobufWriter,
+ curTag: ProtoDesc,
+ descriptor: SerialDescriptor,
+ stream: ByteArrayOutput = ByteArrayOutput()
+) : NestedRepeatedEncoder(proto, writer, curTag, descriptor, stream) {
+
+ // Triggers not writing header
+ override fun SerialDescriptor.getTag(index: Int): ProtoDesc = MISSING_TAG
+
+ override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
+ throw SerializationException("Packing only supports primitive number types")
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+ throw SerializationException("Packing only supports primitive number types")
+ }
+
+ override fun encodeTaggedString(tag: ProtoDesc, value: String) {
+ throw SerializationException("Packing only supports primitive number types")
+ }
+}
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt
new file mode 100644
index 00000000..09773919
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:OptIn(ExperimentalSerializationApi::class)
+@file:Suppress("UNCHECKED_CAST", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+
+package kotlinx.serialization.protobuf.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.internal.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.protobuf.*
+import kotlin.jvm.*
+
+internal open class ProtobufDecoder(
+ @JvmField protected val proto: ProtoBuf,
+ @JvmField protected val reader: ProtobufReader,
+ @JvmField protected val descriptor: SerialDescriptor
+) : ProtobufTaggedDecoder() {
+ override val serializersModule: SerializersModule
+ get() = proto.serializersModule
+
+ // Proto id -> index in serial descriptor cache
+ private var indexCache: IntArray? = null
+ private var sparseIndexCache: MutableMap<Int, Int>? = null
+
+ private var nullValue: Boolean = false
+ private val elementMarker = ElementMarker(descriptor, ::readIfAbsent)
+
+ init {
+ populateCache(descriptor)
+ }
+
+ public fun populateCache(descriptor: SerialDescriptor) {
+ val elements = descriptor.elementsCount
+ if (elements < 32) {
+ /*
+ * If we have reasonably small count of elements, try to build sequential
+ * array for the fast-path. Fast-path implies that elements are not marked with @ProtoId
+ * explicitly or are monotonic and incremental (maybe, 1-indexed)
+ */
+ val cache = IntArray(elements + 1)
+ for (i in 0 until elements) {
+ val protoId = extractProtoId(descriptor, i, false)
+ if (protoId <= elements) {
+ cache[protoId] = i
+ } else {
+ return populateCacheMap(descriptor, elements)
+ }
+ }
+ indexCache = cache
+ } else {
+ populateCacheMap(descriptor, elements)
+ }
+ }
+
+ private fun populateCacheMap(descriptor: SerialDescriptor, elements: Int) {
+ val map = HashMap<Int, Int>(elements)
+ for (i in 0 until elements) {
+ map[extractProtoId(descriptor, i, false)] = i
+ }
+ sparseIndexCache = map
+ }
+
+ private fun getIndexByTag(protoTag: Int): Int {
+ val array = indexCache
+ if (array != null) {
+ return array.getOrElse(protoTag) { -1 }
+ }
+ return getIndexByTagSlowPath(protoTag)
+ }
+
+ private fun getIndexByTagSlowPath(
+ protoTag: Int
+ ): Int = sparseIndexCache!!.getOrElse(protoTag) { -1 }
+
+ private fun findIndexByTag(descriptor: SerialDescriptor, protoTag: Int): Int {
+ // Fast-path: tags are incremental, 1-based
+ if (protoTag < descriptor.elementsCount) {
+ val protoId = extractProtoId(descriptor, protoTag, true)
+ if (protoId == protoTag) return protoTag
+ }
+ return findIndexByTagSlowPath(descriptor, protoTag)
+ }
+
+ private fun findIndexByTagSlowPath(desc: SerialDescriptor, protoTag: Int): Int {
+ for (i in 0 until desc.elementsCount) {
+ val protoId = extractProtoId(desc, i, true)
+ if (protoId == protoTag) return i
+ }
+
+ throw ProtobufDecodingException(
+ "$protoTag is not among valid ${descriptor.serialName} enum proto numbers"
+ )
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ return when (descriptor.kind) {
+ StructureKind.LIST -> {
+ val tag = currentTagOrDefault
+ return if (this.descriptor.kind == StructureKind.LIST && tag != MISSING_TAG && this.descriptor != descriptor) {
+ val reader = makeDelimited(reader, tag)
+ // repeated decoder expects the first tag to be read already
+ reader.readTag()
+ // all elements always have id = 1
+ RepeatedDecoder(proto, reader, ProtoDesc(1, ProtoIntegerType.DEFAULT), descriptor)
+
+ } else if (reader.currentType == SIZE_DELIMITED && descriptor.getElementDescriptor(0).isPackable) {
+ val sliceReader = ProtobufReader(reader.objectInput())
+ PackedArrayDecoder(proto, sliceReader, descriptor)
+
+ } else {
+ RepeatedDecoder(proto, reader, tag, descriptor)
+ }
+ }
+ StructureKind.CLASS, StructureKind.OBJECT, is PolymorphicKind -> {
+ val tag = currentTagOrDefault
+ // Do not create redundant copy
+ if (tag == MISSING_TAG && this.descriptor == descriptor) return this
+ return ProtobufDecoder(proto, makeDelimited(reader, tag), descriptor)
+ }
+ StructureKind.MAP -> MapEntryReader(proto, makeDelimitedForced(reader, currentTagOrDefault), currentTagOrDefault, descriptor)
+ else -> throw SerializationException("Primitives are not supported at top-level")
+ }
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ // Nothing
+ }
+
+ override fun decodeTaggedBoolean(tag: ProtoDesc): Boolean = when(val value = decodeTaggedInt(tag)) {
+ 0 -> false
+ 1 -> true
+ else -> throw SerializationException("Unexpected boolean value: $value")
+ }
+
+ override fun decodeTaggedByte(tag: ProtoDesc): Byte = decodeTaggedInt(tag).toByte()
+ override fun decodeTaggedShort(tag: ProtoDesc): Short = decodeTaggedInt(tag).toShort()
+ override fun decodeTaggedInt(tag: ProtoDesc): Int {
+ return if (tag == MISSING_TAG) {
+ reader.readInt32NoTag()
+ } else {
+ reader.readInt(tag.integerType)
+ }
+ }
+ override fun decodeTaggedLong(tag: ProtoDesc): Long {
+ return if (tag == MISSING_TAG) {
+ reader.readLongNoTag()
+ } else {
+ reader.readLong(tag.integerType)
+ }
+ }
+
+ override fun decodeTaggedFloat(tag: ProtoDesc): Float {
+ return if (tag == MISSING_TAG) {
+ reader.readFloatNoTag()
+ } else {
+ reader.readFloat()
+ }
+ }
+ override fun decodeTaggedDouble(tag: ProtoDesc): Double {
+ return if (tag == MISSING_TAG) {
+ reader.readDoubleNoTag()
+ } else {
+ reader.readDouble()
+ }
+ }
+ override fun decodeTaggedChar(tag: ProtoDesc): Char = decodeTaggedInt(tag).toChar()
+
+ override fun decodeTaggedString(tag: ProtoDesc): String {
+ return if (tag == MISSING_TAG) {
+ reader.readStringNoTag()
+ } else {
+ reader.readString()
+ }
+ }
+
+ override fun decodeTaggedEnum(tag: ProtoDesc, enumDescription: SerialDescriptor): Int {
+ return findIndexByTag(enumDescription, decodeTaggedInt(tag))
+ }
+
+ override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T = decodeSerializableValue(deserializer, null)
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>, previousValue: T?): T = when {
+ deserializer is MapLikeSerializer<*, *, *, *> -> {
+ deserializeMap(deserializer as DeserializationStrategy<T>, previousValue)
+ }
+ deserializer.descriptor == ByteArraySerializer().descriptor -> deserializeByteArray(previousValue as ByteArray?) as T
+ deserializer is AbstractCollectionSerializer<*, *, *> ->
+ (deserializer as AbstractCollectionSerializer<*, T, *>).merge(this, previousValue)
+ else -> deserializer.deserialize(this)
+ }
+
+ private fun deserializeByteArray(previousValue: ByteArray?): ByteArray {
+ val tag = currentTagOrDefault
+ val array = if (tag == MISSING_TAG) {
+ reader.readByteArrayNoTag()
+ } else {
+ reader.readByteArray()
+ }
+ return if (previousValue == null) array else previousValue + array
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun <T> deserializeMap(deserializer: DeserializationStrategy<T>, previousValue: T?): T {
+ val serializer = (deserializer as MapLikeSerializer<Any?, Any?, T, *>)
+ // Yeah thanks different resolution algorithms
+ val mapEntrySerial =
+ kotlinx.serialization.builtins.MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
+ val oldSet = (previousValue as? Map<Any?, Any?>)?.entries
+ val setOfEntries = LinkedHashSetSerializer(mapEntrySerial).merge(this, oldSet)
+ return setOfEntries.associateBy({ it.key }, { it.value }) as T
+ }
+
+ override fun SerialDescriptor.getTag(index: Int) = extractParameters(index)
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ while (true) {
+ val protoId = reader.readTag()
+ if (protoId == -1) { // EOF
+ return elementMarker.nextUnmarkedIndex()
+ }
+ val index = getIndexByTag(protoId)
+ if (index == -1) { // not found
+ reader.skipElement()
+ } else {
+ elementMarker.mark(index)
+ return index
+ }
+ }
+ }
+
+ override fun decodeNotNullMark(): Boolean {
+ return !nullValue
+ }
+
+ private fun readIfAbsent(descriptor: SerialDescriptor, index: Int): Boolean {
+ if (!descriptor.isElementOptional(index)) {
+ val elementDescriptor = descriptor.getElementDescriptor(index)
+ val kind = elementDescriptor.kind
+ if (kind == StructureKind.MAP || kind == StructureKind.LIST) {
+ nullValue = false
+ return true
+ } else if (elementDescriptor.isNullable) {
+ nullValue = true
+ return true
+ }
+ }
+ return false
+ }
+}
+
+private class RepeatedDecoder(
+ proto: ProtoBuf,
+ decoder: ProtobufReader,
+ currentTag: ProtoDesc,
+ descriptor: SerialDescriptor
+) : ProtobufDecoder(proto, decoder, descriptor) {
+ // Current index
+ private var index = -1
+
+ /*
+ * For regular messages, it is always a tag.
+ * For out-of-spec top-level lists (and maps) the very first varint
+ * represents this list size. It is stored in a single variable
+ * as negative value and branched based on that fact.
+ */
+ private val tagOrSize: Long
+
+ init {
+ tagOrSize = if (currentTag == MISSING_TAG) {
+ val length = reader.readInt32NoTag()
+ require(length >= 0) { "Expected positive length for $descriptor, but got $length" }
+ -length.toLong()
+ } else {
+ currentTag
+ }
+ }
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (tagOrSize > 0) {
+ return decodeTaggedListIndex()
+ }
+ return decodeListIndexNoTag()
+ }
+
+ private fun decodeListIndexNoTag(): Int {
+ val size = -tagOrSize
+ val idx = ++index
+ // Check for eof is here for the case that it is an out-of-spec packed array where size is bytesize not list length.
+ if (idx.toLong() == size || reader.eof) return CompositeDecoder.DECODE_DONE
+ return idx
+ }
+
+ private fun decodeTaggedListIndex(): Int {
+ val protoId = if (index == -1) {
+ // For the very first element tag is already read by the parent
+ reader.currentId
+ } else {
+ reader.readTag()
+ }
+
+ return if (protoId == tagOrSize.protoId) {
+ ++index
+ } else {
+ // If we read tag of a different message, push it back to the reader and bail out
+ reader.pushBackTag()
+ CompositeDecoder.DECODE_DONE
+ }
+ }
+
+ override fun SerialDescriptor.getTag(index: Int): ProtoDesc {
+ if (tagOrSize > 0) return tagOrSize
+ return MISSING_TAG
+ }
+}
+
+private class MapEntryReader(
+ proto: ProtoBuf,
+ decoder: ProtobufReader,
+ @JvmField val parentTag: ProtoDesc,
+ descriptor: SerialDescriptor
+) : ProtobufDecoder(proto, decoder, descriptor) {
+ override fun SerialDescriptor.getTag(index: Int): ProtoDesc =
+ if (index % 2 == 0) ProtoDesc(1, (parentTag.integerType))
+ else ProtoDesc(2, (parentTag.integerType))
+}
+
+private fun makeDelimited(decoder: ProtobufReader, parentTag: ProtoDesc): ProtobufReader {
+ val tagless = parentTag == MISSING_TAG
+ val input = if (tagless) decoder.objectTaglessInput() else decoder.objectInput()
+ return ProtobufReader(input)
+}
+
+private fun makeDelimitedForced(decoder: ProtobufReader, parentTag: ProtoDesc): ProtobufReader {
+ val tagless = parentTag == MISSING_TAG
+ val input = if (tagless) decoder.objectTaglessInput() else decoder.objectInput()
+ return ProtobufReader(input)
+}
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt
new file mode 100644
index 00000000..fab7a09d
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+package kotlinx.serialization.protobuf.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.internal.*
+
+import kotlinx.serialization.protobuf.*
+import kotlin.jvm.*
+
+internal open class ProtobufEncoder(
+ @JvmField protected val proto: ProtoBuf,
+ private val writer: ProtobufWriter,
+ @JvmField protected val descriptor: SerialDescriptor
+) : ProtobufTaggedEncoder() {
+ @OptIn(ExperimentalSerializationApi::class) // KT-46731
+ public override val serializersModule
+ get() = proto.serializersModule
+
+ override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean = proto.encodeDefaults
+
+ override fun beginCollection(
+ descriptor: SerialDescriptor,
+ collectionSize: Int
+ ): CompositeEncoder = when (descriptor.kind) {
+ StructureKind.LIST -> {
+ val tag = currentTagOrDefault
+ if (tag.isPacked && descriptor.getElementDescriptor(0).isPackable) {
+ PackedArrayEncoder(proto, writer, currentTagOrDefault, descriptor)
+ } else {
+ if (tag == MISSING_TAG) {
+ writer.writeInt(collectionSize)
+ }
+ if (this.descriptor.kind == StructureKind.LIST && tag != MISSING_TAG && this.descriptor != descriptor) {
+ NestedRepeatedEncoder(proto, writer, tag, descriptor)
+ } else {
+ RepeatedEncoder(proto, writer, tag, descriptor)
+ }
+ }
+ }
+ StructureKind.MAP -> {
+ // Size and missing tag are managed by the implementation that delegated to the list
+ MapRepeatedEncoder(proto, currentTag, writer, descriptor)
+ }
+ else -> throw SerializationException("This serial kind is not supported as collection: $descriptor")
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder = when (descriptor.kind) {
+ StructureKind.LIST -> {
+ if (descriptor.getElementDescriptor(0).isPackable && currentTagOrDefault.isPacked) {
+ PackedArrayEncoder(proto, writer, currentTagOrDefault, descriptor)
+ } else {
+ RepeatedEncoder(proto, writer, currentTagOrDefault, descriptor)
+ }
+ }
+ StructureKind.CLASS, StructureKind.OBJECT, is PolymorphicKind -> {
+ val tag = currentTagOrDefault
+ if (tag == MISSING_TAG && descriptor == this.descriptor) this
+ else ObjectEncoder(proto, currentTagOrDefault, writer, descriptor = descriptor)
+ }
+ StructureKind.MAP -> MapRepeatedEncoder(proto, currentTagOrDefault, writer, descriptor)
+ else -> throw SerializationException("This serial kind is not supported as structure: $descriptor")
+ }
+
+ override fun encodeTaggedInt(tag: ProtoDesc, value: Int) {
+ if (tag == MISSING_TAG) {
+ writer.writeInt(value)
+ } else {
+ writer.writeInt(value, tag.protoId, tag.integerType)
+ }
+ }
+
+ override fun encodeTaggedByte(tag: ProtoDesc, value: Byte) = encodeTaggedInt(tag, value.toInt())
+ override fun encodeTaggedShort(tag: ProtoDesc, value: Short) = encodeTaggedInt(tag, value.toInt())
+ override fun encodeTaggedBoolean(tag: ProtoDesc, value: Boolean) = encodeTaggedInt(tag, if (value) 1 else 0)
+ override fun encodeTaggedChar(tag: ProtoDesc, value: Char) = encodeTaggedInt(tag, value.code)
+
+ override fun encodeTaggedLong(tag: ProtoDesc, value: Long) {
+ if (tag == MISSING_TAG) {
+ writer.writeLong(value)
+ } else {
+ writer.writeLong(value, tag.protoId, tag.integerType)
+ }
+ }
+
+ override fun encodeTaggedFloat(tag: ProtoDesc, value: Float) {
+ if (tag == MISSING_TAG) {
+ writer.writeFloat(value)
+ } else {
+ writer.writeFloat(value, tag.protoId)
+ }
+ }
+
+ override fun encodeTaggedDouble(tag: ProtoDesc, value: Double) {
+ if (tag == MISSING_TAG) {
+ writer.writeDouble(value)
+ } else {
+ writer.writeDouble(value, tag.protoId)
+ }
+ }
+
+ override fun encodeTaggedString(tag: ProtoDesc, value: String) {
+ if (tag == MISSING_TAG) {
+ writer.writeString(value)
+ } else {
+ writer.writeString(value, tag.protoId)
+ }
+ }
+
+ override fun encodeTaggedEnum(
+ tag: ProtoDesc,
+ enumDescriptor: SerialDescriptor,
+ ordinal: Int
+ ) {
+ if (tag == MISSING_TAG) {
+ writer.writeInt(extractProtoId(enumDescriptor, ordinal, zeroBasedDefault = true))
+ } else {
+ writer.writeInt(
+ extractProtoId(enumDescriptor, ordinal, zeroBasedDefault = true),
+ tag.protoId,
+ ProtoIntegerType.DEFAULT
+ )
+ }
+ }
+
+ override fun SerialDescriptor.getTag(index: Int) = extractParameters(index)
+
+ override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when {
+ serializer is MapLikeSerializer<*, *, *, *> -> {
+ serializeMap(serializer as SerializationStrategy<T>, value)
+ }
+ serializer.descriptor == ByteArraySerializer().descriptor -> serializeByteArray(value as ByteArray)
+ else -> serializer.serialize(this, value)
+ }
+
+ private fun serializeByteArray(value: ByteArray) {
+ val tag = popTagOrDefault()
+ if (tag == MISSING_TAG) {
+ writer.writeBytes(value)
+ } else {
+ writer.writeBytes(value, tag.protoId)
+ }
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun <T> serializeMap(serializer: SerializationStrategy<T>, value: T) {
+ // encode maps as collection of map entries, not merged collection of key-values
+ val casted = (serializer as MapLikeSerializer<Any?, Any?, T, *>)
+ val mapEntrySerial = kotlinx.serialization.builtins.MapEntrySerializer(casted.keySerializer, casted.valueSerializer)
+ SetSerializer(mapEntrySerial).serialize(this, (value as Map<*, *>).entries)
+ }
+}
+
+private open class ObjectEncoder(
+ proto: ProtoBuf,
+ @JvmField protected val parentTag: ProtoDesc,
+ @JvmField protected val parentWriter: ProtobufWriter,
+ @JvmField protected val stream: ByteArrayOutput = ByteArrayOutput(),
+ descriptor: SerialDescriptor
+) : ProtobufEncoder(proto, ProtobufWriter(stream), descriptor) {
+ override fun endEncode(descriptor: SerialDescriptor) {
+ // TODO this is exactly the lookahead scenario
+ if (parentTag != MISSING_TAG) {
+ parentWriter.writeOutput(stream, parentTag.protoId)
+ } else {
+ parentWriter.writeOutput(stream)
+ }
+ }
+}
+
+private class MapRepeatedEncoder(
+ proto: ProtoBuf,
+ parentTag: ProtoDesc,
+ parentWriter: ProtobufWriter,
+ descriptor: SerialDescriptor
+) : ObjectEncoder(proto, parentTag, parentWriter, descriptor = descriptor) {
+ override fun SerialDescriptor.getTag(index: Int): ProtoDesc =
+ if (index % 2 == 0) ProtoDesc(1, (parentTag.integerType))
+ else ProtoDesc(2, (parentTag.integerType))
+}
+
+private class RepeatedEncoder(
+ proto: ProtoBuf,
+ writer: ProtobufWriter,
+ @JvmField val curTag: ProtoDesc,
+ descriptor: SerialDescriptor
+) : ProtobufEncoder(proto, writer, descriptor) {
+ override fun SerialDescriptor.getTag(index: Int) = curTag
+}
+
+internal open class NestedRepeatedEncoder(
+ proto: ProtoBuf,
+ @JvmField val writer: ProtobufWriter,
+ @JvmField val curTag: ProtoDesc,
+ descriptor: SerialDescriptor,
+ @JvmField val stream: ByteArrayOutput = ByteArrayOutput()
+) : ProtobufEncoder(proto, ProtobufWriter(stream), descriptor) {
+ // all elements always have id = 1
+ @OptIn(ExperimentalSerializationApi::class) // KT-46731
+ override fun SerialDescriptor.getTag(index: Int) = ProtoDesc(1, ProtoIntegerType.DEFAULT)
+
+ override fun endEncode(descriptor: SerialDescriptor) {
+ writer.writeOutput(stream, curTag.protoId)
+ }
+}
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt
new file mode 100644
index 00000000..c7d4ea08
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.protobuf.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+import kotlin.jvm.*
+
+internal class ProtobufReader(private val input: ByteArrayInput) {
+ @JvmField
+ public var currentId = -1
+ @JvmField
+ public var currentType = -1
+ private var pushBack = false
+ private var pushBackHeader = 0
+
+ public val eof
+ get() = !pushBack && input.availableBytes == 0
+
+ public fun readTag(): Int {
+ if (pushBack) {
+ pushBack = false
+ val previousHeader = (currentId shl 3) or currentType
+ return updateIdAndType(pushBackHeader).also {
+ pushBackHeader = previousHeader
+ }
+ }
+ // Header to use when pushed back is the old id/type
+ pushBackHeader = (currentId shl 3) or currentType
+
+ val header = input.readVarint64(true).toInt()
+ return updateIdAndType(header)
+ }
+
+ private fun updateIdAndType(header: Int): Int {
+ return if (header == -1) {
+ currentId = -1
+ currentType = -1
+ -1
+ } else {
+ currentId = header ushr 3
+ currentType = header and 0b111
+ currentId
+ }
+ }
+
+ public fun pushBackTag() {
+ pushBack = true
+
+ val nextHeader = (currentId shl 3) or currentType
+ updateIdAndType(pushBackHeader)
+ pushBackHeader = nextHeader
+ }
+
+ fun skipElement() {
+ when (currentType) {
+ VARINT -> readInt(ProtoIntegerType.DEFAULT)
+ i64 -> readLong(ProtoIntegerType.FIXED)
+ SIZE_DELIMITED -> readByteArray()
+ i32 -> readInt(ProtoIntegerType.FIXED)
+ else -> throw ProtobufDecodingException("Unsupported start group or end group wire type: $currentType")
+ }
+ }
+
+ @Suppress("NOTHING_TO_INLINE")
+ private inline fun assertWireType(expected: Int) {
+ if (currentType != expected) throw ProtobufDecodingException("Expected wire type $expected, but found $currentType")
+ }
+
+ fun readByteArray(): ByteArray {
+ assertWireType(SIZE_DELIMITED)
+ return readByteArrayNoTag()
+ }
+
+ fun readByteArrayNoTag(): ByteArray {
+ val length = decode32()
+ checkLength(length)
+ return input.readExactNBytes(length)
+ }
+
+ fun objectInput(): ByteArrayInput {
+ assertWireType(SIZE_DELIMITED)
+ return objectTaglessInput()
+ }
+
+ fun objectTaglessInput(): ByteArrayInput {
+ val length = decode32()
+ checkLength(length)
+ return input.slice(length)
+ }
+
+ fun readInt(format: ProtoIntegerType): Int {
+ val wireType = if (format == ProtoIntegerType.FIXED) i32 else VARINT
+ assertWireType(wireType)
+ return decode32(format)
+ }
+
+ fun readInt32NoTag(): Int = decode32()
+
+ fun readLong(format: ProtoIntegerType): Long {
+ val wireType = if (format == ProtoIntegerType.FIXED) i64 else VARINT
+ assertWireType(wireType)
+ return decode64(format)
+ }
+
+ fun readLongNoTag(): Long = decode64(ProtoIntegerType.DEFAULT)
+
+ fun readFloat(): Float {
+ assertWireType(i32)
+ return Float.fromBits(readIntLittleEndian())
+ }
+
+ fun readFloatNoTag(): Float = Float.fromBits(readIntLittleEndian())
+
+ private fun readIntLittleEndian(): Int {
+ // TODO this could be optimized by extracting method to the IS
+ var result = 0
+ for (i in 0..3) {
+ val byte = input.read() and 0x000000FF
+ result = result or (byte shl (i * 8))
+ }
+ return result
+ }
+
+ private fun readLongLittleEndian(): Long {
+ // TODO this could be optimized by extracting method to the IS
+ var result = 0L
+ for (i in 0..7) {
+ val byte = (input.read() and 0x000000FF).toLong()
+ result = result or (byte shl (i * 8))
+ }
+ return result
+ }
+
+ fun readDouble(): Double {
+ assertWireType(i64)
+ return Double.fromBits(readLongLittleEndian())
+ }
+
+ fun readDoubleNoTag(): Double {
+ return Double.fromBits(readLongLittleEndian())
+ }
+
+ fun readString(): String {
+ assertWireType(SIZE_DELIMITED)
+ val length = decode32()
+ checkLength(length)
+ return input.readString(length)
+ }
+
+ fun readStringNoTag(): String {
+ val length = decode32()
+ checkLength(length)
+ return input.readString(length)
+ }
+
+ private fun checkLength(length: Int) {
+ if (length < 0) {
+ throw ProtobufDecodingException("Unexpected negative length: $length")
+ }
+ }
+
+ private fun decode32(format: ProtoIntegerType = ProtoIntegerType.DEFAULT): Int = when (format) {
+ ProtoIntegerType.DEFAULT -> input.readVarint64(false).toInt()
+ ProtoIntegerType.SIGNED -> decodeSignedVarintInt(
+ input
+ )
+ ProtoIntegerType.FIXED -> readIntLittleEndian()
+ }
+
+ private fun decode64(format: ProtoIntegerType = ProtoIntegerType.DEFAULT): Long = when (format) {
+ ProtoIntegerType.DEFAULT -> input.readVarint64(false)
+ ProtoIntegerType.SIGNED -> decodeSignedVarintLong(
+ input
+ )
+ ProtoIntegerType.FIXED -> readLongLittleEndian()
+ }
+
+ /**
+ * Source for all varint operations:
+ * https://github.com/addthis/stream-lib/blob/master/src/main/java/com/clearspring/analytics/util/Varint.java
+ */
+ private fun decodeSignedVarintInt(input: ByteArrayInput): Int {
+ val raw = input.readVarint32()
+ val temp = raw shl 31 shr 31 xor raw shr 1
+ // This extra step lets us deal with the largest signed values by treating
+ // negative results from read unsigned methods as like unsigned values.
+ // Must re-flip the top bit if the original read value had it set.
+ return temp xor (raw and (1 shl 31))
+ }
+
+ private fun decodeSignedVarintLong(input: ByteArrayInput): Long {
+ val raw = input.readVarint64(false)
+ val temp = raw shl 63 shr 63 xor raw shr 1
+ // This extra step lets us deal with the largest signed values by treating
+ // negative results from read unsigned methods as like unsigned values
+ // Must re-flip the top bit if the original read value had it set.
+ return temp xor (raw and (1L shl 63))
+
+ }
+}
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedBase.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedBase.kt
new file mode 100644
index 00000000..5c016435
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedBase.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf.internal
+
+import kotlinx.serialization.*
+import kotlin.jvm.*
+
+/*
+ * In ProtoBuf spec, ids from 19000 to 19999 are reserved for protocol use,
+ * thus we are leveraging it here and use 19_500 as a marker no one us allowed to use.
+ * It does not leak to the resulting output.
+ */
+internal const val MISSING_TAG = 19_500L
+
+internal abstract class ProtobufTaggedBase {
+ private var tagsStack = LongArray(8)
+ @JvmField
+ protected var stackSize = -1
+
+ protected val currentTag: ProtoDesc
+ get() = tagsStack[stackSize]
+
+ protected val currentTagOrDefault: ProtoDesc
+ get() = if (stackSize == -1) MISSING_TAG else tagsStack[stackSize]
+
+ protected fun popTagOrDefault(): ProtoDesc = if (stackSize == -1) MISSING_TAG else tagsStack[stackSize--]
+
+ protected fun pushTag(tag: ProtoDesc) {
+ if (tag == MISSING_TAG) return // Missing tag is never added
+ val idx = ++stackSize
+ if (stackSize >= tagsStack.size) {
+ expand()
+ }
+ tagsStack[idx] = tag
+ }
+
+ private fun expand() {
+ tagsStack = tagsStack.copyOf(tagsStack.size * 2)
+ }
+
+ protected fun popTag(): ProtoDesc {
+ if (stackSize >= 0) return tagsStack[stackSize--]
+ // Unreachable
+ throw SerializationException("No tag in stack for requested element")
+ }
+
+ protected inline fun <E> tagBlock(tag: ProtoDesc, block: () -> E): E {
+ pushTag(tag)
+ return block()
+ }
+}
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedDecoder.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedDecoder.kt
new file mode 100644
index 00000000..8a5a3827
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedDecoder.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.protobuf.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+internal abstract class ProtobufTaggedDecoder : ProtobufTaggedBase(), Decoder, CompositeDecoder {
+ protected abstract fun SerialDescriptor.getTag(index: Int): ProtoDesc
+
+ protected abstract fun decodeTaggedBoolean(tag: ProtoDesc): Boolean
+ protected abstract fun decodeTaggedByte(tag: ProtoDesc): Byte
+ protected abstract fun decodeTaggedShort(tag: ProtoDesc): Short
+ protected abstract fun decodeTaggedInt(tag: ProtoDesc): Int
+ protected abstract fun decodeTaggedLong(tag: ProtoDesc): Long
+ protected abstract fun decodeTaggedFloat(tag: ProtoDesc): Float
+ protected abstract fun decodeTaggedDouble(tag: ProtoDesc): Double
+ protected abstract fun decodeTaggedChar(tag: ProtoDesc): Char
+ protected abstract fun decodeTaggedString(tag: ProtoDesc): String
+ protected abstract fun decodeTaggedEnum(tag: ProtoDesc, enumDescription: SerialDescriptor): Int
+ protected abstract fun <T : Any?> decodeSerializableValue(deserializer: DeserializationStrategy<T>, previousValue: T?): T
+
+ protected open fun decodeTaggedInline(tag: ProtoDesc, inlineDescriptor: SerialDescriptor): Decoder = this.apply { pushTag(tag) }
+
+ override fun decodeNotNullMark(): Boolean = true
+ final override fun decodeNull(): Nothing? = null
+ final override fun decodeBoolean(): Boolean = decodeTaggedBoolean(popTagOrDefault())
+ final override fun decodeByte(): Byte = decodeTaggedByte(popTagOrDefault())
+ final override fun decodeShort(): Short = decodeTaggedShort(popTagOrDefault())
+ final override fun decodeInt(): Int = decodeTaggedInt(popTagOrDefault())
+ final override fun decodeLong(): Long = decodeTaggedLong(popTagOrDefault())
+ final override fun decodeFloat(): Float = decodeTaggedFloat(popTagOrDefault())
+ final override fun decodeDouble(): Double = decodeTaggedDouble(popTagOrDefault())
+ final override fun decodeChar(): Char = decodeTaggedChar(popTagOrDefault())
+ final override fun decodeString(): String = decodeTaggedString(popTagOrDefault())
+ final override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeTaggedEnum(popTagOrDefault(), enumDescriptor)
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ return this
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ // Nothing
+ }
+
+ final override fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean =
+ decodeTaggedBoolean(descriptor.getTag(index))
+
+ final override fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte =
+ decodeTaggedByte(descriptor.getTag(index))
+
+ final override fun decodeShortElement(descriptor: SerialDescriptor, index: Int): Short =
+ decodeTaggedShort(descriptor.getTag(index))
+
+ final override fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int =
+ decodeTaggedInt(descriptor.getTag(index))
+
+ final override fun decodeLongElement(descriptor: SerialDescriptor, index: Int): Long =
+ decodeTaggedLong(descriptor.getTag(index))
+
+ final override fun decodeFloatElement(descriptor: SerialDescriptor, index: Int): Float =
+ decodeTaggedFloat(descriptor.getTag(index))
+
+ final override fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int): Double =
+ decodeTaggedDouble(descriptor.getTag(index))
+
+ final override fun decodeCharElement(descriptor: SerialDescriptor, index: Int): Char =
+ decodeTaggedChar(descriptor.getTag(index))
+
+ final override fun decodeStringElement(descriptor: SerialDescriptor, index: Int): String =
+ decodeTaggedString(descriptor.getTag(index))
+
+ final override fun <T : Any?> decodeSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ deserializer: DeserializationStrategy<T>,
+ previousValue: T?
+ ): T = tagBlock(descriptor.getTag(index)) { decodeSerializableValue(deserializer, previousValue) }
+
+ final override fun <T : Any> decodeNullableSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ deserializer: DeserializationStrategy<T?>,
+ previousValue: T?
+ ): T? = tagBlock(descriptor.getTag(index)) {
+ if (decodeNotNullMark()) {
+ decodeSerializableValue(deserializer, previousValue)
+ } else {
+ decodeNull()
+ }
+ }
+
+ override fun decodeInline(inlineDescriptor: SerialDescriptor): Decoder {
+ return decodeTaggedInline(popTag(), inlineDescriptor)
+ }
+
+ override fun decodeInlineElement(
+ descriptor: SerialDescriptor,
+ index: Int
+ ): Decoder {
+ return decodeTaggedInline(descriptor.getTag(index), descriptor.getElementDescriptor(index))
+ }
+}
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt
new file mode 100644
index 00000000..01532df3
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+@OptIn(ExperimentalSerializationApi::class)
+internal abstract class ProtobufTaggedEncoder : ProtobufTaggedBase(), Encoder, CompositeEncoder {
+ private enum class NullableMode {
+ ACCEPTABLE,
+ OPTIONAL,
+ COLLECTION,
+ NOT_NULL
+ }
+ private var nullableMode: NullableMode = NullableMode.NOT_NULL
+
+ protected abstract fun SerialDescriptor.getTag(index: Int): ProtoDesc
+
+ protected abstract fun encodeTaggedInt(tag: ProtoDesc, value: Int)
+ protected abstract fun encodeTaggedByte(tag: ProtoDesc, value: Byte)
+ protected abstract fun encodeTaggedShort(tag: ProtoDesc, value: Short)
+ protected abstract fun encodeTaggedLong(tag: ProtoDesc, value: Long)
+ protected abstract fun encodeTaggedFloat(tag: ProtoDesc, value: Float)
+ protected abstract fun encodeTaggedDouble(tag: ProtoDesc, value: Double)
+ protected abstract fun encodeTaggedBoolean(tag: ProtoDesc, value: Boolean)
+ protected abstract fun encodeTaggedChar(tag: ProtoDesc, value: Char)
+ protected abstract fun encodeTaggedString(tag: ProtoDesc, value: String)
+ protected abstract fun encodeTaggedEnum(tag: ProtoDesc, enumDescriptor: SerialDescriptor, ordinal: Int)
+
+ protected open fun encodeTaggedInline(tag: ProtoDesc, inlineDescriptor: SerialDescriptor): Encoder = this.apply { pushTag(tag) }
+
+ public final override fun encodeNull() {
+ if (nullableMode != NullableMode.ACCEPTABLE) {
+ val message = when (nullableMode) {
+ NullableMode.OPTIONAL -> "'null' is not supported for optional properties in ProtoBuf"
+ NullableMode.COLLECTION -> "'null' is not supported for collection types in ProtoBuf"
+ NullableMode.NOT_NULL -> "'null' is not allowed for not-null properties"
+ else -> "'null' is not supported in ProtoBuf";
+ }
+ throw SerializationException(message)
+ }
+ }
+
+ public final override fun encodeBoolean(value: Boolean) {
+ encodeTaggedBoolean(popTagOrDefault(), value)
+ }
+
+ public final override fun encodeByte(value: Byte) {
+ encodeTaggedByte(popTagOrDefault(), value)
+ }
+
+ public final override fun encodeShort(value: Short) {
+ encodeTaggedShort(popTagOrDefault(), value)
+ }
+
+ public final override fun encodeInt(value: Int) {
+ encodeTaggedInt(popTagOrDefault(), value)
+ }
+
+ public final override fun encodeLong(value: Long) {
+ encodeTaggedLong(popTagOrDefault(), value)
+ }
+
+ public final override fun encodeFloat(value: Float) {
+ encodeTaggedFloat(popTagOrDefault(), value)
+ }
+
+ public final override fun encodeDouble(value: Double) {
+ encodeTaggedDouble(popTagOrDefault(), value)
+ }
+
+ public final override fun encodeChar(value: Char) {
+ encodeTaggedChar(popTagOrDefault(), value)
+ }
+
+ public final override fun encodeString(value: String) {
+ encodeTaggedString(popTagOrDefault(), value)
+ }
+
+ public final override fun encodeEnum(
+ enumDescriptor: SerialDescriptor,
+ index: Int
+ ): Unit = encodeTaggedEnum(popTagOrDefault(), enumDescriptor, index)
+
+
+ public final override fun endStructure(descriptor: SerialDescriptor) {
+ if (stackSize >= 0) {
+ popTag()
+ }
+ endEncode(descriptor)
+ }
+
+ protected open fun endEncode(descriptor: SerialDescriptor) {}
+
+ public final override fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean): Unit =
+ encodeTaggedBoolean(descriptor.getTag(index), value)
+
+ public final override fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte): Unit =
+ encodeTaggedByte(descriptor.getTag(index), value)
+
+ public final override fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short): Unit =
+ encodeTaggedShort(descriptor.getTag(index), value)
+
+ public final override fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int): Unit =
+ encodeTaggedInt(descriptor.getTag(index), value)
+
+ public final override fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long): Unit =
+ encodeTaggedLong(descriptor.getTag(index), value)
+
+ public final override fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float): Unit =
+ encodeTaggedFloat(descriptor.getTag(index), value)
+
+ public final override fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double): Unit =
+ encodeTaggedDouble(descriptor.getTag(index), value)
+
+ public final override fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char): Unit =
+ encodeTaggedChar(descriptor.getTag(index), value)
+
+ public final override fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String): Unit =
+ encodeTaggedString(descriptor.getTag(index), value)
+
+ public final override fun <T : Any?> encodeSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ serializer: SerializationStrategy<T>,
+ value: T
+ ) {
+ nullableMode = NullableMode.NOT_NULL
+
+ pushTag(descriptor.getTag(index))
+ encodeSerializableValue(serializer, value)
+ }
+
+ public final override fun <T : Any> encodeNullableSerializableElement(
+ descriptor: SerialDescriptor,
+ index: Int,
+ serializer: SerializationStrategy<T>,
+ value: T?
+ ) {
+ val elementKind = descriptor.getElementDescriptor(index).kind
+ nullableMode = if (descriptor.isElementOptional(index)) {
+ NullableMode.OPTIONAL
+ } else if (elementKind == StructureKind.MAP || elementKind == StructureKind.LIST) {
+ NullableMode.COLLECTION
+ } else {
+ NullableMode.ACCEPTABLE
+ }
+
+ pushTag(descriptor.getTag(index))
+ encodeNullableSerializableValue(serializer, value)
+ }
+
+ override fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder {
+ return encodeTaggedInline(popTag(), inlineDescriptor)
+ }
+
+ override fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder {
+ return encodeTaggedInline(descriptor.getTag(index), descriptor.getElementDescriptor(index))
+ }
+}
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt
new file mode 100644
index 00000000..ba164272
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.protobuf.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+
+internal class ProtobufWriter(private val out: ByteArrayOutput) {
+ fun writeBytes(bytes: ByteArray, tag: Int) {
+ out.encode32((tag shl 3) or SIZE_DELIMITED)
+ writeBytes(bytes)
+ }
+
+ fun writeBytes(bytes: ByteArray) {
+ out.encode32(bytes.size)
+ out.write(bytes)
+ }
+
+ fun writeOutput(output: ByteArrayOutput, tag: Int) {
+ out.encode32((tag shl 3) or SIZE_DELIMITED)
+ writeOutput(output)
+ }
+
+ fun writeOutput(output: ByteArrayOutput) {
+ out.encode32(output.size())
+ out.write(output)
+ }
+
+ fun writeInt(value: Int, tag: Int, format: ProtoIntegerType) {
+ val wireType = if (format == ProtoIntegerType.FIXED) i32 else VARINT
+ out.encode32((tag shl 3) or wireType)
+ out.encode32(value, format)
+ }
+
+ fun writeInt(value: Int) {
+ out.encode32(value)
+ }
+
+ fun writeLong(value: Long, tag: Int, format: ProtoIntegerType) {
+ val wireType = if (format == ProtoIntegerType.FIXED) i64 else VARINT
+ out.encode32((tag shl 3) or wireType)
+ out.encode64(value, format)
+ }
+
+ fun writeLong(value: Long) {
+ out.encode64(value)
+ }
+
+ fun writeString(value: String, tag: Int) {
+ val bytes = value.encodeToByteArray()
+ writeBytes(bytes, tag)
+ }
+
+ fun writeString(value: String) {
+ val bytes = value.encodeToByteArray()
+ writeBytes(bytes)
+ }
+
+ fun writeDouble(value: Double, tag: Int) {
+ out.encode32((tag shl 3) or i64)
+ out.writeLong(value.reverseBytes())
+ }
+
+ fun writeDouble(value: Double) {
+ out.writeLong(value.reverseBytes())
+ }
+
+ fun writeFloat(value: Float, tag: Int) {
+ out.encode32((tag shl 3) or i32)
+ out.writeInt(value.reverseBytes())
+ }
+
+ fun writeFloat(value: Float) {
+ out.writeInt(value.reverseBytes())
+ }
+
+ private fun ByteArrayOutput.encode32(
+ number: Int,
+ format: ProtoIntegerType = ProtoIntegerType.DEFAULT
+ ) {
+ when (format) {
+ ProtoIntegerType.FIXED -> out.writeInt(number.reverseBytes())
+ ProtoIntegerType.DEFAULT -> encodeVarint64(number.toLong())
+ ProtoIntegerType.SIGNED -> encodeVarint32(((number shl 1) xor (number shr 31)))
+ }
+ }
+
+ private fun ByteArrayOutput.encode64(number: Long, format: ProtoIntegerType = ProtoIntegerType.DEFAULT) {
+ when (format) {
+ ProtoIntegerType.FIXED -> out.writeLong(number.reverseBytes())
+ ProtoIntegerType.DEFAULT -> encodeVarint64(number)
+ ProtoIntegerType.SIGNED -> encodeVarint64((number shl 1) xor (number shr 63))
+ }
+ }
+
+ private fun Float.reverseBytes(): Int = toRawBits().reverseBytes()
+
+ private fun Double.reverseBytes(): Long = toRawBits().reverseBytes()
+}
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Streams.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Streams.kt
new file mode 100644
index 00000000..575c5e74
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Streams.kt
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf.internal
+
+import kotlinx.serialization.*
+
+internal class ByteArrayInput(private var array: ByteArray, private val endIndex: Int = array.size) {
+ private var position: Int = 0
+ val availableBytes: Int get() = endIndex - position
+
+ fun slice(size: Int): ByteArrayInput {
+ ensureEnoughBytes(size)
+ val result = ByteArrayInput(array, position + size)
+ result.position = position
+ position += size
+ return result
+ }
+
+ fun read(): Int {
+ return if (position < endIndex) array[position++].toInt() and 0xFF else -1
+ }
+
+ fun readExactNBytes(bytesCount: Int): ByteArray {
+ ensureEnoughBytes(bytesCount)
+ val b = ByteArray(bytesCount)
+ val length = b.size
+ // Are there any bytes available?
+ val copied = if (endIndex - position < length) endIndex - position else length
+ array.copyInto(destination = b, destinationOffset = 0, startIndex = position, endIndex = position + copied)
+ position += copied
+ return b
+ }
+
+ private fun ensureEnoughBytes(bytesCount: Int) {
+ if (bytesCount > availableBytes) {
+ throw SerializationException("Unexpected EOF, available $availableBytes bytes, requested: $bytesCount")
+ }
+ }
+
+ fun readString(length: Int): String {
+ val result = array.decodeToString(position, position + length)
+ position += length
+ return result
+ }
+
+ fun readVarint32(): Int {
+ if (position == endIndex) {
+ eof()
+ }
+
+ // Fast-path: unrolled loop for single and two byte values
+ var currentPosition = position
+ var result = array[currentPosition++].toInt()
+ if (result >= 0) {
+ position = currentPosition
+ return result
+ } else if (endIndex - position > 1) {
+ result = result xor (array[currentPosition++].toInt() shl 7)
+ if (result < 0) {
+ position = currentPosition
+ return result xor (0.inv() shl 7)
+ }
+ }
+
+ return readVarint32SlowPath()
+ }
+
+ fun readVarint64(eofAllowed: Boolean): Long {
+ if (position == endIndex) {
+ if (eofAllowed) return -1
+ else eof()
+ }
+
+ // Fast-path: single and two byte values
+ var currentPosition = position
+ var result = array[currentPosition++].toLong()
+ if (result >= 0) {
+ position = currentPosition
+ return result
+ } else if (endIndex - position > 1) {
+ result = result xor (array[currentPosition++].toLong() shl 7)
+ if (result < 0) {
+ position = currentPosition
+ return result xor (0L.inv() shl 7)
+ }
+ }
+
+ return readVarint64SlowPath()
+ }
+
+ private fun eof() {
+ throw SerializationException("Unexpected EOF")
+ }
+
+ private fun readVarint64SlowPath(): Long {
+ var result = 0L
+ var shift = 0
+ while (shift < 64) {
+ val byte = read()
+ result = result or ((byte and 0x7F).toLong() shl shift)
+ if (byte and 0x80 == 0) {
+ return result
+ }
+ shift += 7
+ }
+ throw SerializationException("Input stream is malformed: Varint too long (exceeded 64 bits)")
+ }
+
+ private fun readVarint32SlowPath(): Int {
+ var result = 0
+ var shift = 0
+ while (shift < 32) {
+ val byte = read()
+ result = result or ((byte and 0x7F) shl shift)
+ if (byte and 0x80 == 0) {
+ return result
+ }
+ shift += 7
+ }
+ throw SerializationException("Input stream is malformed: Varint too long (exceeded 32 bits)")
+ }
+}
+
+internal class ByteArrayOutput {
+
+ private companion object {
+ /*
+ * Map number of leading zeroes -> varint size
+ * See the explanation in this blogpost: https://richardstartin.github.io/posts/dont-use-protobuf-for-telemetry
+ */
+ private val VAR_INT_LENGTHS = IntArray(65) {
+ (63 - it) / 7
+ }
+ }
+
+ private var array: ByteArray = ByteArray(32)
+ private var position: Int = 0
+
+ private fun ensureCapacity(elementsToAppend: Int) {
+ if (position + elementsToAppend <= array.size) {
+ return
+ }
+ val newArray = ByteArray((position + elementsToAppend).takeHighestOneBit() shl 1)
+ array.copyInto(newArray)
+ array = newArray
+ }
+
+ fun size(): Int {
+ return position
+ }
+
+ fun toByteArray(): ByteArray {
+ val newArray = ByteArray(position)
+ array.copyInto(newArray, startIndex = 0, endIndex = this.position)
+ return newArray
+ }
+
+ fun write(buffer: ByteArray) {
+ val count = buffer.size
+ if (count == 0) {
+ return
+ }
+
+ ensureCapacity(count)
+ buffer.copyInto(
+ destination = array,
+ destinationOffset = this.position,
+ startIndex = 0,
+ endIndex = count
+ )
+ this.position += count
+ }
+
+ fun write(output: ByteArrayOutput) {
+ val count = output.size()
+ ensureCapacity(count)
+ output.array.copyInto(
+ destination = array,
+ destinationOffset = this.position,
+ startIndex = 0,
+ endIndex = count
+ )
+ this.position += count
+ }
+
+ fun writeInt(intValue: Int) {
+ ensureCapacity(4)
+ for (i in 3 downTo 0) {
+ array[position++] = (intValue shr i * 8).toByte()
+ }
+ }
+
+ fun writeLong(longValue: Long) {
+ ensureCapacity(8)
+ for (i in 7 downTo 0) {
+ array[position++] = (longValue shr i * 8).toByte()
+ }
+ }
+
+ fun encodeVarint32(value: Int) {
+ // Fast-path: unrolled loop for single byte
+ ensureCapacity(5)
+ if (value and 0x7F.inv() == 0) {
+ array[position++] = value.toByte()
+ return
+ }
+ val length = varIntLength(value.toLong())
+ encodeVarint(value.toLong(), length)
+ }
+
+ fun encodeVarint64(value: Long) {
+ val length = varIntLength(value)
+ ensureCapacity(length + 1)
+ encodeVarint(value, length)
+ }
+
+ private fun encodeVarint(value: Long, length: Int) {
+ var current = value
+ for (i in 0 until length) {
+ array[position + i] = ((current and 0x7F) or 0x80).toByte()
+ current = current ushr 7
+ }
+ array[position + length] = current.toByte()
+ position += length + 1
+ }
+
+ private fun varIntLength(value: Long): Int {
+ return VAR_INT_LENGTHS[value.countLeadingZeroBits()]
+ }
+}
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator.kt
new file mode 100644
index 00000000..e54370fb
--- /dev/null
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator.kt
@@ -0,0 +1,525 @@
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package kotlinx.serialization.protobuf.schema
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.protobuf.*
+import kotlinx.serialization.protobuf.internal.*
+
+/**
+ * Experimental generator of ProtoBuf schema that is compatible with [serializable][Serializable] Kotlin classes
+ * and data encoded and decoded by [ProtoBuf] format.
+ *
+ * The schema is generated based on provided [SerialDescriptor] and is compatible with proto2 schema definition.
+ * An arbitrary Kotlin class represent much wider object domain than the ProtoBuf specification, thus schema generator
+ * has the following list of restrictions:
+ *
+ * * Serial name of the class and all its fields should be a valid Proto [identifier](https://developers.google.com/protocol-buffers/docs/reference/proto2-spec)
+ * * Nullable values are allowed only for Kotlin [nullable][SerialDescriptor.isNullable] types, but not [optional][SerialDescriptor.isElementOptional]
+ * in order to properly distinguish "default" and "absent" values.
+ * * The name of the type without the package directive uniquely identifies the proto message type and two or more fields with the same serial name
+ * are considered to have the same type. Schema generator allows to specify a separate package directive for the pack of classes in order
+ * to mitigate this limitation.
+ * * Nested collections, e.g. `List<List<Int>>` are represented using the artificial wrapper message in order to distinguish
+ * repeated fields boundaries.
+ * * Default Kotlin values are not representable in proto schema. A special commentary is generated for properties with default values.
+ * * Empty nullable collections are not supported by the generated schema and will be prohibited in [ProtoBuf] as well
+ * due to their ambiguous nature.
+ *
+ * Temporary restrictions:
+ * * [Contextual] data is represented as as `bytes` type
+ * * [Polymorphic] data is represented as a artificial `KotlinxSerializationPolymorphic` message.
+ *
+ * Other types are mapped according to their specification: primitives as primitives, lists as 'repeated' fields and
+ * maps as 'repeated' map entries.
+ *
+ * The name of messages and enums is extracted from [SerialDescriptor.serialName] in [SerialDescriptor] without the package directive,
+ * as substring after the last dot character, the `'?'` character is also removed if it is present at the end of the string.
+ */
+@ExperimentalSerializationApi
+public object ProtoBufSchemaGenerator {
+
+ /**
+ * Generate text of protocol buffers schema version 2 for the given [rootDescriptor].
+ * The resulting schema will contain all types referred by [rootDescriptor].
+ *
+ * [packageName] define common protobuf package for all messages and enum in the schema, it may contain `'a'`..`'z'`
+ * letters in upper and lower case, decimal digits, `'.'` or `'_'` chars, but must be started only by a letter and
+ * not finished by a dot.
+ *
+ * [options] define values for protobuf options. Option value (map value) is an any string, option name (map key)
+ * should be the same format as [packageName].
+ *
+ * The method throws [IllegalArgumentException] if any of the restrictions imposed by [ProtoBufSchemaGenerator] is violated.
+ */
+ @ExperimentalSerializationApi
+ public fun generateSchemaText(
+ rootDescriptor: SerialDescriptor,
+ packageName: String? = null,
+ options: Map<String, String> = emptyMap()
+ ): String = generateSchemaText(listOf(rootDescriptor), packageName, options)
+
+ /**
+ * Generate text of protocol buffers schema version 2 for the given serializable [descriptors].
+ * [packageName] define common protobuf package for all messages and enum in the schema, it may contain `'a'`..`'z'`
+ * letters in upper and lower case, decimal digits, `'.'` or `'_'` chars, but started only from a letter and
+ * not finished by dot.
+ *
+ * [options] define values for protobuf options. Option value (map value) is an any string, option name (map key)
+ * should be the same format as [packageName].
+ *
+ * The method throws [IllegalArgumentException] if any of the restrictions imposed by [ProtoBufSchemaGenerator] is violated.
+ */
+ @ExperimentalSerializationApi
+ public fun generateSchemaText(
+ descriptors: List<SerialDescriptor>,
+ packageName: String? = null,
+ options: Map<String, String> = emptyMap()
+ ): String {
+ packageName?.let { p -> p.checkIsValidFullIdentifier { "Incorrect protobuf package name '$it'" } }
+ checkDoubles(descriptors)
+ val builder = StringBuilder()
+ builder.generateProto2SchemaText(descriptors, packageName, options)
+ return builder.toString()
+ }
+
+ private fun checkDoubles(descriptors: List<SerialDescriptor>) {
+ val rootTypesNames = mutableSetOf<String>()
+ val duplicates = mutableListOf<String>()
+
+ descriptors.map { it.messageOrEnumName }.forEach {
+ if (!rootTypesNames.add(it)) {
+ duplicates += it
+ }
+ }
+ if (duplicates.isNotEmpty()) {
+ throw IllegalArgumentException("Serial names of the following types are duplicated: $duplicates")
+ }
+ }
+
+ private fun StringBuilder.generateProto2SchemaText(
+ descriptors: List<SerialDescriptor>,
+ packageName: String?,
+ options: Map<String, String>
+ ) {
+ appendLine("""syntax = "proto2";""").appendLine()
+
+ packageName?.let { append("package ").append(it).appendLine(';') }
+
+ for ((optionName, optionValue) in options) {
+ val safeOptionName = removeLineBreaks(optionName)
+ val safeOptionValue = removeLineBreaks(optionValue)
+ safeOptionName.checkIsValidFullIdentifier { "Invalid option name '$it'" }
+ append("option ").append(safeOptionName).append(" = \"").append(safeOptionValue).appendLine("\";")
+ }
+
+ val generatedTypes = mutableSetOf<String>()
+ val queue = ArrayDeque<TypeDefinition>()
+ descriptors.map { TypeDefinition(it) }.forEach { queue.add(it) }
+
+ while (queue.isNotEmpty()) {
+ val type = queue.removeFirst()
+ val descriptor = type.descriptor
+ val name = descriptor.messageOrEnumName
+ if (!generatedTypes.add(name)) {
+ continue
+ }
+
+ appendLine()
+ when {
+ descriptor.isProtobufMessage -> queue.addAll(generateMessage(type))
+ descriptor.isProtobufEnum -> generateEnum(type)
+ else -> throw IllegalStateException(
+ "Unrecognized custom type with serial name "
+ + "'${descriptor.serialName}' and kind '${descriptor.kind}'"
+ )
+ }
+ }
+ }
+
+ private fun StringBuilder.generateMessage(messageType: TypeDefinition): List<TypeDefinition> {
+ val messageDescriptor = messageType.descriptor
+ val messageName: String
+ if (messageType.isSynthetic) {
+ append("// This message was generated to support ").append(messageType.ability)
+ .appendLine(" and does not present in Kotlin.")
+
+ messageName = messageDescriptor.serialName
+ if (messageType.containingMessageName != null) {
+ append("// Containing message '").append(messageType.containingMessageName).append("', field '")
+ .append(messageType.fieldName).appendLine('\'')
+ }
+ } else {
+ messageName = messageDescriptor.messageOrEnumName
+ messageName.checkIsValidIdentifier {
+ "Invalid name for the message in protobuf schema '$messageName'. " +
+ "Serial name of the class '${messageDescriptor.serialName}'"
+ }
+ val safeSerialName = removeLineBreaks(messageDescriptor.serialName)
+ if (safeSerialName != messageName) {
+ append("// serial name '").append(safeSerialName).appendLine('\'')
+ }
+ }
+
+ append("message ").append(messageName).appendLine(" {")
+
+ val usedNumbers: MutableSet<Int> = mutableSetOf()
+ val nestedTypes = mutableListOf<TypeDefinition>()
+ for (index in 0 until messageDescriptor.elementsCount) {
+ val fieldName = messageDescriptor.getElementName(index)
+ fieldName.checkIsValidIdentifier {
+ "Invalid name of the field '$fieldName' in message '$messageName' for class with serial " +
+ "name '${messageDescriptor.serialName}'"
+ }
+
+ val fieldDescriptor = messageDescriptor.getElementDescriptor(index)
+
+ val isList = fieldDescriptor.isProtobufRepeated
+
+ nestedTypes += when {
+ fieldDescriptor.isProtobufNamedType -> generateNamedType(messageType, index)
+ isList -> generateListType(messageType, index)
+ fieldDescriptor.isProtobufMap -> generateMapType(messageType, index)
+ else -> throw IllegalStateException(
+ "Unprocessed message field type with serial name " +
+ "'${fieldDescriptor.serialName}' and kind '${fieldDescriptor.kind}'"
+ )
+ }
+
+
+ val annotations = messageDescriptor.getElementAnnotations(index)
+ val number = annotations.filterIsInstance<ProtoNumber>().singleOrNull()?.number ?: index + 1
+ if (!usedNumbers.add(number)) {
+ throw IllegalArgumentException("Field number $number is repeated in the class with serial name ${messageDescriptor.serialName}")
+ }
+
+ append(' ').append(fieldName).append(" = ").append(number)
+
+ val isPackRequested = annotations.filterIsInstance<ProtoPacked>().singleOrNull() != null
+
+ when {
+ !isPackRequested ||
+ !isList || // ignore as packed only meaningful on repeated types
+ !fieldDescriptor.getElementDescriptor(0).isPackable // Ignore if the type is not allowed to be packed
+ -> appendLine(';')
+ else -> appendLine(" [packed=true];")
+ }
+ }
+ appendLine('}')
+
+ return nestedTypes
+ }
+
+ private fun StringBuilder.generateNamedType(messageType: TypeDefinition, index: Int): List<TypeDefinition> {
+ val messageDescriptor = messageType.descriptor
+
+ val fieldDescriptor = messageDescriptor.getElementDescriptor(index)
+ val nestedTypes: List<TypeDefinition>
+ val typeName: String = when {
+ messageDescriptor.isSealedPolymorphic && index == 1 -> {
+ appendLine(" // decoded as message with one of these types:")
+ nestedTypes = fieldDescriptor.elementDescriptors.map { TypeDefinition(it) }.toList()
+ nestedTypes.forEachIndexed { _, childType ->
+ append(" // message ").append(childType.descriptor.messageOrEnumName).append(", serial name '")
+ .append(removeLineBreaks(childType.descriptor.serialName)).appendLine('\'')
+ }
+ fieldDescriptor.scalarTypeName()
+ }
+ fieldDescriptor.isProtobufScalar -> {
+ nestedTypes = emptyList()
+ fieldDescriptor.scalarTypeName(messageDescriptor.getElementAnnotations(index))
+ }
+ fieldDescriptor.isOpenPolymorphic -> {
+ nestedTypes = listOf(SyntheticPolymorphicType)
+ SyntheticPolymorphicType.descriptor.serialName
+ }
+ else -> {
+ // enum or regular message
+ nestedTypes = listOf(TypeDefinition(fieldDescriptor))
+ fieldDescriptor.messageOrEnumName
+ }
+ }
+
+ if (messageDescriptor.isElementOptional(index)) {
+ appendLine(" // WARNING: a default value decoded when value is missing")
+ }
+ val optional = fieldDescriptor.isNullable || messageDescriptor.isElementOptional(index)
+
+ append(" ").append(if (optional) "optional " else "required ").append(typeName)
+
+ return nestedTypes
+ }
+
+ private fun StringBuilder.generateMapType(messageType: TypeDefinition, index: Int): List<TypeDefinition> {
+ val messageDescriptor = messageType.descriptor
+ val mapDescriptor = messageDescriptor.getElementDescriptor(index)
+ val originalMapValueDescriptor = mapDescriptor.getElementDescriptor(1)
+ val valueType = if (originalMapValueDescriptor.isProtobufCollection) {
+ createNestedCollectionType(messageType, index, originalMapValueDescriptor, "nested collection in map value")
+ } else {
+ TypeDefinition(originalMapValueDescriptor)
+ }
+ val valueDescriptor = valueType.descriptor
+
+ if (originalMapValueDescriptor.isNullable) {
+ appendLine(" // WARNING: nullable map values can not be represented in protobuf")
+ }
+ generateCollectionAbsenceComment(messageDescriptor, mapDescriptor, index)
+
+ val keyTypeName = mapDescriptor.getElementDescriptor(0).scalarTypeName(mapDescriptor.getElementAnnotations(0))
+ val valueTypeName = valueDescriptor.protobufTypeName(mapDescriptor.getElementAnnotations(1))
+ append(" map<").append(keyTypeName).append(", ").append(valueTypeName).append(">")
+
+ return if (valueDescriptor.isProtobufMessageOrEnum) {
+ listOf(valueType)
+ } else {
+ emptyList()
+ }
+ }
+
+ private fun StringBuilder.generateListType(messageType: TypeDefinition, index: Int): List<TypeDefinition> {
+ val messageDescriptor = messageType.descriptor
+ val collectionDescriptor = messageDescriptor.getElementDescriptor(index)
+ val originalElementDescriptor = collectionDescriptor.getElementDescriptor(0)
+ val elementType = if (collectionDescriptor.kind == StructureKind.LIST) {
+ if (originalElementDescriptor.isProtobufCollection) {
+ createNestedCollectionType(messageType, index, originalElementDescriptor, "nested collection in list")
+ } else {
+ TypeDefinition(originalElementDescriptor)
+ }
+ } else {
+ createLegacyMapType(messageType, index, "legacy map")
+ }
+
+ val elementDescriptor = elementType.descriptor
+
+ if (elementDescriptor.isNullable) {
+ appendLine(" // WARNING: nullable elements of collections can not be represented in protobuf")
+ }
+ generateCollectionAbsenceComment(messageDescriptor, collectionDescriptor, index)
+
+ val typeName = elementDescriptor.protobufTypeName(messageDescriptor.getElementAnnotations(index))
+ append(" repeated ").append(typeName)
+
+ return if (elementDescriptor.isProtobufMessageOrEnum) {
+ listOf(elementType)
+ } else {
+ emptyList()
+ }
+ }
+
+ private fun StringBuilder.generateEnum(enumType: TypeDefinition) {
+ val enumDescriptor = enumType.descriptor
+ val enumName = enumDescriptor.messageOrEnumName
+ enumName.checkIsValidIdentifier {
+ "Invalid name for the enum in protobuf schema '$enumName'. Serial name of the enum " +
+ "class '${enumDescriptor.serialName}'"
+ }
+ val safeSerialName = removeLineBreaks(enumDescriptor.serialName)
+ if (safeSerialName != enumName) {
+ append("// serial name '").append(enumName).appendLine('\'')
+ }
+
+ append("enum ").append(enumName).appendLine(" {")
+
+ enumDescriptor.elementDescriptors.forEachIndexed { number, element ->
+ val elementName = element.protobufEnumElementName
+ elementName.checkIsValidIdentifier {
+ "The enum element name '$elementName' is invalid in the " +
+ "protobuf schema. Serial name of the enum class '${enumDescriptor.serialName}'"
+ }
+ append(" ").append(elementName).append(" = ").append(number).appendLine(';')
+ }
+ appendLine('}')
+ }
+
+ private val SerialDescriptor.isOpenPolymorphic: Boolean
+ get() = kind == PolymorphicKind.OPEN
+
+ private val SerialDescriptor.isSealedPolymorphic: Boolean
+ get() = kind == PolymorphicKind.SEALED
+
+ private val SerialDescriptor.isProtobufNamedType: Boolean
+ get() = isProtobufMessageOrEnum || isProtobufScalar
+
+ private val SerialDescriptor.isProtobufScalar: Boolean
+ get() = (kind is PrimitiveKind)
+ || (kind is StructureKind.LIST && getElementDescriptor(0).kind === PrimitiveKind.BYTE)
+ || kind == SerialKind.CONTEXTUAL
+
+ private val SerialDescriptor.isProtobufMessageOrEnum: Boolean
+ get() = isProtobufMessage || isProtobufEnum
+
+ private val SerialDescriptor.isProtobufMessage: Boolean
+ get() = kind == StructureKind.CLASS || kind == StructureKind.OBJECT || kind == PolymorphicKind.SEALED || kind == PolymorphicKind.OPEN
+
+ private val SerialDescriptor.isProtobufCollection: Boolean
+ get() = isProtobufRepeated || isProtobufMap
+
+ private val SerialDescriptor.isProtobufRepeated: Boolean
+ get() = (kind == StructureKind.LIST && getElementDescriptor(0).kind != PrimitiveKind.BYTE)
+ || (kind == StructureKind.MAP && !getElementDescriptor(0).isValidMapKey)
+
+ private val SerialDescriptor.isProtobufMap: Boolean
+ get() = kind == StructureKind.MAP && getElementDescriptor(0).isValidMapKey
+
+ private val SerialDescriptor.isProtobufEnum: Boolean
+ get() = kind == SerialKind.ENUM
+
+ private val SerialDescriptor.isValidMapKey: Boolean
+ get() = kind == PrimitiveKind.INT || kind == PrimitiveKind.LONG || kind == PrimitiveKind.BOOLEAN || kind == PrimitiveKind.STRING
+
+
+ private val SerialDescriptor.messageOrEnumName: String
+ get() = (serialName.substringAfterLast('.', serialName)).removeSuffix("?")
+
+ private fun SerialDescriptor.protobufTypeName(annotations: List<Annotation> = emptyList()): String {
+ return if (isProtobufScalar) {
+ scalarTypeName(annotations)
+ } else {
+ messageOrEnumName
+ }
+ }
+
+ private val SerialDescriptor.protobufEnumElementName: String
+ get() = serialName.substringAfterLast('.', serialName)
+
+ private fun SerialDescriptor.scalarTypeName(annotations: List<Annotation> = emptyList()): String {
+ val integerType = annotations.filterIsInstance<ProtoType>().firstOrNull()?.type ?: ProtoIntegerType.DEFAULT
+
+ if (kind == SerialKind.CONTEXTUAL) {
+ return "bytes"
+ }
+
+ if (kind is StructureKind.LIST && getElementDescriptor(0).kind == PrimitiveKind.BYTE) {
+ return "bytes"
+ }
+
+ return when (kind as PrimitiveKind) {
+ PrimitiveKind.BOOLEAN -> "bool"
+ PrimitiveKind.BYTE, PrimitiveKind.CHAR, PrimitiveKind.SHORT, PrimitiveKind.INT ->
+ when (integerType) {
+ ProtoIntegerType.DEFAULT -> "int32"
+ ProtoIntegerType.SIGNED -> "sint32"
+ ProtoIntegerType.FIXED -> "fixed32"
+ }
+ PrimitiveKind.LONG ->
+ when (integerType) {
+ ProtoIntegerType.DEFAULT -> "int64"
+ ProtoIntegerType.SIGNED -> "sint64"
+ ProtoIntegerType.FIXED -> "fixed64"
+ }
+ PrimitiveKind.FLOAT -> "float"
+ PrimitiveKind.DOUBLE -> "double"
+ PrimitiveKind.STRING -> "string"
+ }
+ }
+
+ private data class TypeDefinition(
+ val descriptor: SerialDescriptor,
+ val isSynthetic: Boolean = false,
+ val ability: String? = null,
+ val containingMessageName: String? = null,
+ val fieldName: String? = null
+ )
+
+ private val SyntheticPolymorphicType = TypeDefinition(
+ buildClassSerialDescriptor("KotlinxSerializationPolymorphic") {
+ element("type", PrimitiveSerialDescriptor("typeDescriptor", PrimitiveKind.STRING))
+ element("value", buildSerialDescriptor("valueDescriptor", StructureKind.LIST) {
+ element("0", Byte.serializer().descriptor)
+ })
+ },
+ true,
+ "polymorphic types"
+ )
+
+ private class NotNullSerialDescriptor(val original: SerialDescriptor) : SerialDescriptor by original {
+ override val isNullable = false
+ }
+
+ private val SerialDescriptor.notNull get() = NotNullSerialDescriptor(this)
+
+ private fun StringBuilder.generateCollectionAbsenceComment(
+ messageDescriptor: SerialDescriptor,
+ collectionDescriptor: SerialDescriptor,
+ index: Int
+ ) {
+ if (!collectionDescriptor.isNullable && messageDescriptor.isElementOptional(index)) {
+ appendLine(" // WARNING: a default value decoded when value is missing")
+ } else if (collectionDescriptor.isNullable && !messageDescriptor.isElementOptional(index)) {
+ appendLine(" // WARNING: an empty collection decoded when a value is missing")
+ } else if (collectionDescriptor.isNullable && messageDescriptor.isElementOptional(index)) {
+ appendLine(" // WARNING: a default value decoded when value is missing")
+ }
+ }
+
+ private fun createLegacyMapType(
+ messageType: TypeDefinition,
+ index: Int,
+ description: String
+ ): TypeDefinition {
+ val messageDescriptor = messageType.descriptor
+ val fieldDescriptor = messageDescriptor.getElementDescriptor(index)
+ val fieldName = messageDescriptor.getElementName(index)
+ val messageName = messageDescriptor.messageOrEnumName
+
+ val wrapperName = "${messageName}_${fieldName}"
+ val wrapperDescriptor = buildClassSerialDescriptor(wrapperName) {
+ element("key", fieldDescriptor.getElementDescriptor(0).notNull)
+ element("value", fieldDescriptor.getElementDescriptor(1).notNull)
+ }
+
+ return TypeDefinition(
+ wrapperDescriptor,
+ true,
+ description,
+ messageType.containingMessageName ?: messageName,
+ messageType.fieldName ?: fieldName
+ )
+ }
+
+ private fun createNestedCollectionType(
+ messageType: TypeDefinition,
+ index: Int,
+ elementDescriptor: SerialDescriptor,
+ description: String
+ ): TypeDefinition {
+ val messageDescriptor = messageType.descriptor
+ val fieldName = messageDescriptor.getElementName(index)
+ val messageName = messageDescriptor.messageOrEnumName
+
+ val wrapperName = "${messageName}_${fieldName}"
+ val wrapperDescriptor = buildClassSerialDescriptor(wrapperName) {
+ element("value", elementDescriptor.notNull)
+ }
+
+ return TypeDefinition(
+ wrapperDescriptor,
+ true,
+ description,
+ messageType.containingMessageName ?: messageName,
+ messageType.fieldName ?: fieldName
+ )
+ }
+
+ private fun removeLineBreaks(text: String): String {
+ return text.replace('\n', ' ').replace('\r', ' ')
+ }
+
+ private val IDENTIFIER_REGEX = Regex("[A-Za-z][A-Za-z0-9_]*")
+
+ private fun String.checkIsValidFullIdentifier(messageSupplier: (String) -> String) {
+ if (split('.').any { !it.matches(IDENTIFIER_REGEX) }) {
+ throw IllegalArgumentException(messageSupplier.invoke(this))
+ }
+ }
+
+ private fun String.checkIsValidIdentifier(messageSupplier: () -> String) {
+ if (!matches(IDENTIFIER_REGEX)) {
+ throw IllegalArgumentException(messageSupplier.invoke())
+ }
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/HexConverter.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/HexConverter.kt
new file mode 100644
index 00000000..280dd540
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/HexConverter.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+object HexConverter {
+ private const val hexCode = "0123456789ABCDEF"
+
+ fun printHexBinary(data: ByteArray, lowerCase: Boolean = false): String {
+ val r = StringBuilder(data.size * 2)
+ for (b in data) {
+ r.append(hexCode[b.toInt() shr 4 and 0xF])
+ r.append(hexCode[b.toInt() and 0xF])
+ }
+ return if (lowerCase) r.toString().lowercase() else r.toString()
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
new file mode 100644
index 00000000..7ef97293
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.protobuf.*
+import kotlin.native.concurrent.*
+
+@Serializable
+open class PolyBase(@ProtoNumber(1) val id: Int) {
+ override fun hashCode(): Int {
+ return id
+ }
+
+ override fun toString(): String {
+ return "PolyBase(id=$id)"
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as PolyBase
+
+ if (id != other.id) return false
+
+ return true
+ }
+
+}
+
+@Serializable
+data class PolyDerived(@ProtoNumber(2) val s: String) : PolyBase(1)
+
+@Serializable
+abstract class SimpleAbstract
+
+@Serializable
+data class SimpleIntInheritor(val i: Int, val s: String) : SimpleAbstract()
+
+@Serializable
+data class SimpleStringInheritor(val s: String, val i: Int) : SimpleAbstract()
+
+@Serializable
+data class PolyBox(@Polymorphic val boxed: SimpleAbstract)
+
+@SharedImmutable
+val SimplePolymorphicModule = SerializersModule {
+ polymorphic(SimpleAbstract::class) {
+ subclass(SimpleIntInheritor.serializer())
+ subclass(SimpleStringInheritor.serializer())
+ }
+}
+
+@Serializable
+data class SealedBox(val boxed: List<SimpleSealed>)
+
+@Serializable
+sealed class SimpleSealed {
+ @Serializable
+ public data class SubSealedA(val s: String) : SimpleSealed()
+
+ @Serializable
+ public data class SubSealedB(val i: Int) : SimpleSealed()
+}
+
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/TestUtils.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/TestUtils.kt
new file mode 100644
index 00000000..d4d8929e
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/TestUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlin.test.*
+
+internal inline fun <reified T> assertSerializedToBinaryAndRestored(
+ original: T,
+ serializer: KSerializer<T>,
+ format: BinaryFormat,
+ printResult: Boolean = false,
+ hexResultToCheck: String? = null
+) {
+ val bytes = format.encodeToByteArray(serializer, original)
+ val hexString = HexConverter.printHexBinary(bytes, lowerCase = true)
+ if (printResult) {
+ println("[Serialized form] $hexString")
+ }
+ if (hexResultToCheck != null) {
+ assertEquals(
+ hexResultToCheck.lowercase(),
+ hexString,
+ "Expected serialized binary to be equal in hex representation"
+ )
+ }
+ val restored = format.decodeFromByteArray(serializer, bytes)
+ if (printResult) println("[Restored form] $restored")
+ assertEquals(original, restored)
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/AutoAssignIdsTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/AutoAssignIdsTest.kt
new file mode 100644
index 00000000..31b3798a
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/AutoAssignIdsTest.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class AutoAssignIdsTest {
+ @Serializable
+ data class WithoutIds(val a: Int, val b: String)
+
+ @Serializable
+ data class WithId(@ProtoNumber(1) val a: Int, @ProtoNumber(2) val b: String)
+
+ @Test
+ fun saveAndRestoreWithoutIds() {
+ val w1 = WithoutIds(1, "foo")
+ val bytes = ProtoBuf.encodeToByteArray(WithoutIds.serializer(), w1)
+ val w2 = ProtoBuf.decodeFromByteArray(WithoutIds.serializer(), bytes)
+ assertEquals(w1, w2)
+ }
+
+ @Test
+ fun incrementalIds() {
+ val w1 = WithoutIds(1, "foo")
+ val bytes = ProtoBuf.encodeToByteArray(WithoutIds.serializer(), w1)
+ val w2 = ProtoBuf.decodeFromByteArray(WithId.serializer(), bytes)
+ assertEquals(w1.a, w2.a)
+ assertEquals(w1.b, w2.b)
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ByteArraySerializerTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ByteArraySerializerTest.kt
new file mode 100644
index 00000000..2b21179a
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ByteArraySerializerTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.random.*
+import kotlin.test.*
+
+class ByteArraySerializerTest {
+
+ @Serializable
+ class ByteArrayCarrier(@ProtoNumber(2) val data: ByteArray) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as ByteArrayCarrier
+
+ if (!data.contentEquals(other.data)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ return data.contentHashCode()
+ }
+
+ override fun toString(): String {
+ return "ByteArrayCarrier(data=${data.contentToString()})"
+ }
+ }
+
+ @Test
+ fun testByteArrayProtobuf() {
+ val obj = ByteArrayCarrier(byteArrayOf(42, 100))
+ val s = ProtoBuf.encodeToHexString(ByteArrayCarrier.serializer(), obj)
+ assertEquals("""12022a64""", s)
+ val obj2 = ProtoBuf.decodeFromHexString(ByteArrayCarrier.serializer(), s)
+ assertEquals(obj, obj2)
+ }
+
+ @Test
+ fun testWrappedByteArrayProtobuf() {
+ val arraySize = 301
+ val arr = Random.nextBytes(ByteArray(arraySize))
+ val obj = ByteArrayCarrier(arr)
+ val bytes = ProtoBuf.encodeToByteArray(ByteArrayCarrier.serializer(), obj)
+ assertEquals(obj, ProtoBuf.decodeFromByteArray(ByteArrayCarrier.serializer(), bytes))
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomSerializersProtobufTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomSerializersProtobufTest.kt
new file mode 100644
index 00000000..99a49838
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomSerializersProtobufTest.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class CustomSerializersProtobufTest {
+
+ private fun protoBufWithB() = ProtoBuf {
+ serializersModule = serializersModuleOf(B::class, BSerializer)
+ }
+
+ @Test
+ fun writeCustom() {
+ val a = A(B(2))
+ val j = protoBufWithB()
+ val s = j.encodeToHexString(a).uppercase()
+ assertEquals("0802", s)
+ }
+
+ @Test
+ fun readCustom() {
+ val a = A(B(2))
+ val j = protoBufWithB()
+ val s = j.decodeFromHexString<A>("0802")
+ assertEquals(a, s)
+ }
+
+ @Test
+ fun writeCustomList() {
+ val obj = BList(listOf(B(1), B(2), B(3)))
+ val j = protoBufWithB()
+ val s = j.encodeToHexString(obj).uppercase()
+ assertEquals("080108020803", s)
+ }
+
+ @Test
+ fun readCustomList() {
+ val obj = BList(listOf(B(1), B(2), B(3)))
+ val j = protoBufWithB()
+ val bs = j.decodeFromHexString<BList>("080108020803")
+ assertEquals(obj, bs)
+ }
+
+ @Test
+ fun writeCustomInvertedOrder() {
+ val obj = C(1, 2)
+ val s = ProtoBuf.encodeToHexString(CSerializer, obj).uppercase()
+ assertEquals("10020801", s)
+ }
+
+ @Test
+ fun readCustomInvertedOrder() {
+ val obj = C(1, 2)
+ val s = ProtoBuf.decodeFromHexString<C>("10020801")
+ assertEquals(obj, s)
+ }
+
+ @Test
+ fun writeCustomOmitDefault() {
+ val obj = C(b = 2)
+ val s = ProtoBuf.encodeToHexString(CSerializer, obj).uppercase()
+ assertEquals("1002", s)
+ }
+
+ @Test
+ fun readCustomOmitDefault() {
+ val obj = C(b = 2)
+ val s = ProtoBuf.decodeFromHexString<C>("1002")
+ assertEquals(obj, s)
+ }
+
+ @Test
+ fun writeOptionalList1() {
+ val obj = CList1(listOf(C(a = 1), C(b = 2), C(3, 4)))
+ val s = ProtoBuf.encodeToHexString(CList1Serializer, obj).uppercase()
+ assertEquals("0A04102A08010A0210020A0410040803", s)
+ }
+
+ @Test
+ fun readOptionalList1() {
+ val obj = CList1(listOf(C(a = 1), C(b = 2), C(3, 4)))
+ val j = "0A04102A08010A0210020A0410040803"
+ assertEquals(obj, ProtoBuf.decodeFromHexString(j))
+ }
+
+ @Test
+ fun writeOptionalList2a() {
+ val obj = CList2(7, listOf(C(a = 5), C(b = 6), C(7, 8)))
+ val s = ProtoBuf.encodeToHexString(CList2Serializer, obj).uppercase()
+ assertEquals("1204102A0805120210061204100808070807", s)
+ }
+
+ @Test
+ fun readOptionalList2a() {
+ val obj = CList2(7, listOf(C(a = 5), C(b = 6), C(7, 8)))
+ val j = "08071204102A080512021006120410080807"
+ assertEquals(obj, ProtoBuf.decodeFromHexString(CList2Serializer, j))
+ }
+
+ @Test
+ fun writeOptionalList2b() {
+ val obj = CList2(c = listOf(C(a = 5), C(b = 6), C(7, 8)))
+ val s = ProtoBuf.encodeToHexString(CList2Serializer, obj).uppercase()
+ assertEquals("1204102A080512021006120410080807", s)
+ }
+
+ @Test
+ fun readOptionalList2b() {
+ val obj = CList2(c = listOf(C(a = 5), C(b = 6), C(7, 8)))
+ val j = "1204102A080512021006120410080807"
+ assertEquals(obj, ProtoBuf.decodeFromHexString(CList2Serializer, j))
+ }
+
+ @Test
+ fun writeOptionalList3a() {
+ val obj = CList3(listOf(C(a = 1), C(b = 2), C(3, 4)), 99)
+ val s = ProtoBuf.encodeToHexString(CList3Serializer, obj).uppercase()
+ assertEquals("0A04102A08010A0210020A04100408031063", s)
+ }
+
+ @Test
+ fun readOptionalList3a() {
+ val obj = CList3(listOf(C(a = 1), C(b = 2), C(3, 4)), 99)
+ val j = "10630A04102A08010A0210020A0410040803"
+ assertEquals(obj, ProtoBuf.decodeFromHexString(CList3Serializer, j))
+ }
+
+ @Test
+ fun writeOptionalList3b() {
+ val obj = CList3(f = 99)
+ val s = ProtoBuf.encodeToHexString(CList3Serializer, obj).uppercase()
+ assertEquals("1063", s)
+ }
+
+ @Test
+ fun readOptionalList3b() {
+ val obj = CList3(f = 99)
+ val j = "1063"
+ assertEquals(obj, ProtoBuf.decodeFromHexString(CList3Serializer, j))
+ }
+
+ @Test
+ fun writeOptionalList4a() {
+ val obj = CList4(listOf(C(a = 1), C(b = 2), C(3, 4)), 54)
+ val s = ProtoBuf.encodeToHexString(CList4Serializer, obj).uppercase()
+ assertEquals("10360A04102A08010A0210020A0410040803", s)
+ }
+
+ @Test
+ fun readOptionalList4a() {
+ val obj = CList4(listOf(C(a = 1), C(b = 2), C(3, 4)), 54)
+ val j = "10360A04102A08010A0210020A0410040803"
+ assertEquals(obj, ProtoBuf.decodeFromHexString(CList4Serializer, j))
+ }
+
+ @Test
+ fun writeOptionalList4b() {
+ val obj = CList4(h = 97)
+ val j = "1061"
+ val s = ProtoBuf.encodeToHexString(CList4Serializer, obj).uppercase()
+ assertEquals(j, s)
+ }
+
+ @Test
+ fun readOptionalList4b() {
+ val obj = CList4(h = 97)
+ val j = "1061"
+ assertEquals(obj, ProtoBuf.decodeFromHexString(CList4Serializer, j))
+ }
+
+ @Test
+ fun writeOptionalList5a() {
+ val obj = CList5(listOf(9, 8, 7, 6, 5), 5)
+ val s = ProtoBuf.encodeToHexString(CList5Serializer, obj).uppercase()
+ assertEquals("100508090808080708060805", s)
+ }
+
+ @Test
+ fun readOptionalList5a() {
+ val obj = CList5(listOf(9, 8, 7, 6, 5), 5)
+ val j = "100508090808080708060805"
+ assertEquals(obj, ProtoBuf.decodeFromHexString(CList5Serializer, j))
+ }
+
+ @Test
+ fun writeOptionalList5b() {
+ val obj = CList5(h = 999)
+ val s = ProtoBuf.encodeToHexString(CList5Serializer, obj).uppercase()
+ assertEquals("10E707", s)
+ }
+
+ @Test
+ fun readOptionalList5b() {
+ val obj = CList5(h = 999)
+ val j = "10E707"
+ assertEquals(obj, ProtoBuf.decodeFromHexString(CList5Serializer, j))
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomizedSerializableTestClasses.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomizedSerializableTestClasses.kt
new file mode 100644
index 00000000..24e5543e
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomizedSerializableTestClasses.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:UseContextualSerialization(B::class)
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+@Serializable
+data class A(@ProtoNumber(1) val b: B)
+
+data class B(@ProtoNumber(1) val value: Int)
+
+object BSerializer : KSerializer<B> {
+ override fun serialize(encoder: Encoder, value: B) {
+ encoder.encodeInt(value.value)
+ }
+
+ override fun deserialize(decoder: Decoder): B {
+ return B(decoder.decodeInt())
+ }
+
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("B", PrimitiveKind.INT)
+}
+
+@Serializable
+data class BList(@ProtoNumber(1) val bs: List<B>)
+
+@Serializable
+data class C(@ProtoNumber(1) val a: Int = 31, @ProtoNumber(2) val b: Int = 42)
+
+object CSerializer : KSerializer<C> {
+ override fun serialize(encoder: Encoder, value: C) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ elemOutput.encodeIntElement(descriptor, 1, value.b)
+ if (value.a != 31) elemOutput.encodeIntElement(descriptor, 0, value.a)
+ elemOutput.endStructure(descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): C {
+ return C.serializer().deserialize(decoder)
+ }
+
+ override val descriptor: SerialDescriptor = C.serializer().descriptor
+}
+
+object CList1Serializer : KSerializer<CList1> {
+ override fun serialize(encoder: Encoder, value: CList1) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(CSerializer), value.c)
+ elemOutput.endStructure(descriptor)
+ }
+ override fun deserialize(decoder: Decoder): CList1 {
+ return CList1.serializer().deserialize(decoder)
+ }
+ override val descriptor: SerialDescriptor = CList1.serializer().descriptor
+}
+@Serializable
+data class CList1(@ProtoNumber(1) val c: List<C>)
+
+object CList2Serializer : KSerializer<CList2> {
+ override fun serialize(encoder: Encoder, value: CList2) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ elemOutput.encodeSerializableElement(descriptor, 1, ListSerializer(CSerializer), value.c)
+ if (value.d != 5) elemOutput.encodeIntElement(descriptor, 0, value.d)
+ elemOutput.endStructure(descriptor)
+ }
+ override fun deserialize(decoder: Decoder): CList2 {
+ return CList2.serializer().deserialize(decoder)
+ }
+ override val descriptor: SerialDescriptor = CList2.serializer().descriptor
+}
+
+@Serializable
+data class CList2(@ProtoNumber(1) val d: Int = 5, @ProtoNumber(2) val c: List<C>)
+
+object CList3Serializer : KSerializer<CList3> {
+ override fun serialize(encoder: Encoder, value: CList3) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ if (value.e.isNotEmpty()) elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(CSerializer), value.e)
+ elemOutput.encodeIntElement(descriptor, 1, value.f)
+ elemOutput.endStructure(descriptor)
+ }
+ override fun deserialize(decoder: Decoder): CList3 {
+ return CList3.serializer().deserialize(decoder)
+ }
+ override val descriptor: SerialDescriptor = CList3.serializer().descriptor
+}
+
+@Serializable
+data class CList3(@ProtoNumber(1) val e: List<C> = emptyList(), @ProtoNumber(2) val f: Int)
+
+object CList4Serializer : KSerializer<CList4> {
+ override fun serialize(encoder: Encoder, value: CList4) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ elemOutput.encodeIntElement(descriptor, 1, value.h)
+ if (value.g.isNotEmpty()) elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(CSerializer), value.g)
+ elemOutput.endStructure(descriptor)
+ }
+ override fun deserialize(decoder: Decoder): CList4 {
+ return CList4.serializer().deserialize(decoder)
+ }
+ override val descriptor: SerialDescriptor = CList4.serializer().descriptor
+}
+@Serializable
+data class CList4(@ProtoNumber(1) val g: List<C> = emptyList(), @ProtoNumber(2) val h: Int)
+
+object CList5Serializer : KSerializer<CList5> {
+ override fun serialize(encoder: Encoder, value: CList5) {
+ val elemOutput = encoder.beginStructure(descriptor)
+ elemOutput.encodeIntElement(descriptor, 1, value.h)
+ if (value.g.isNotEmpty()) elemOutput.encodeSerializableElement(
+ descriptor, 0,
+ ListSerializer(Int.serializer()),
+ value.g
+ )
+ elemOutput.endStructure(descriptor)
+ }
+ override fun deserialize(decoder: Decoder): CList5 {
+ return CList5.serializer().deserialize(decoder)
+ }
+ override val descriptor: SerialDescriptor = CList5.serializer().descriptor
+}
+@Serializable
+data class CList5(@ProtoNumber(1) val g: List<Int> = emptyList(), @ProtoNumber(2) val h: Int)
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/MapEntryTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/MapEntryTest.kt
new file mode 100644
index 00000000..b61af699
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/MapEntryTest.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class MapEntryTest {
+
+ @Serializable
+ data class Wrapper(val e: Map.Entry<Int, Int>)
+
+ @Test
+ fun testEntry() {
+ val e = Wrapper(mapOf(1 to 1).entries.single())
+ val output = ProtoBuf.encodeToHexString(Wrapper.serializer(), e)
+ assertEquals("0a0408011001", output)
+ assertEquals(e.e.toPair(), ProtoBuf.decodeFromHexString(Wrapper.serializer(), output).e.toPair())
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt
new file mode 100644
index 00000000..e7bf6762
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class PackedArraySerializerTest {
+
+ abstract class BaseFloatArrayCarrier {
+ abstract val createdAt: ULong
+ abstract val vector: FloatArray
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is BaseFloatArrayCarrier) return false
+
+ if (createdAt != other.createdAt) return false
+ if (!vector.contentEquals(other.vector)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = createdAt.hashCode()
+ result = 31 * result + vector.contentHashCode()
+ return result
+ }
+ }
+
+ @Serializable
+ class PackedFloatArrayCarrier(
+ @ProtoNumber(2)
+ override val createdAt: ULong,
+ @ProtoPacked
+ @ProtoNumber(3) override val vector: FloatArray
+ ) : BaseFloatArrayCarrier()
+
+ @Serializable
+ class NonPackedFloatArrayCarrier(
+ @ProtoNumber(2)
+ override val createdAt: ULong,
+ @ProtoNumber(3) override val vector: FloatArray
+ ) : BaseFloatArrayCarrier()
+
+ @Serializable
+ data class PackedStringCarrier(
+ @ProtoNumber(0)
+ @ProtoPacked
+ val s: List<String>
+ )
+
+ /**
+ * Test that when packing is specified the array is encoded as packed
+ */
+ @Test
+ fun testEncodePackedFloatArrayProtobuf() {
+ val obj = PackedFloatArrayCarrier(1234567890L.toULong(), floatArrayOf(1f, 2f, 3f))
+ val s = ProtoBuf.encodeToHexString(PackedFloatArrayCarrier.serializer(), obj).uppercase()
+ assertEquals("""10D285D8CC041A0C0000803F0000004000004040""", s)
+ }
+
+ /**
+ * Test that when packing is not specified the array is not encoded as packed. Note that protobuf 3
+ * should encode as packed by default. The format doesn't allow specifying versions at this point so
+ * the default remains the original.
+ */
+ @Test
+ fun testEncodeNonPackedFloatArrayProtobuf() {
+ val obj = NonPackedFloatArrayCarrier(1234567890L.toULong(), floatArrayOf(1f, 2f, 3f))
+ val s = ProtoBuf.encodeToHexString(NonPackedFloatArrayCarrier.serializer(), obj).uppercase()
+ assertEquals("""10D285D8CC041D0000803F1D000000401D00004040""", s)
+ }
+
+ /**
+ * Per the specification decoders should support both packed and repeated fields independent of whether
+ * a field is specified as packed in the schema. Check that decoding works with both types (packed and non-packed)
+ * if the data itself is packed.
+ */
+ @Test
+ fun testDecodePackedFloatArrayProtobuf() {
+ val obj: BaseFloatArrayCarrier = PackedFloatArrayCarrier(1234567890L.toULong(), floatArrayOf(1f, 2f, 3f))
+ val s = """10D285D8CC041A0C0000803F0000004000004040"""
+ val decodedPacked = ProtoBuf.decodeFromHexString(PackedFloatArrayCarrier.serializer(), s)
+ assertEquals(obj, decodedPacked)
+ val decodedNonPacked = ProtoBuf.decodeFromHexString(NonPackedFloatArrayCarrier.serializer(), s)
+ assertEquals(obj, decodedNonPacked)
+ }
+
+ /**
+ * Per the specification decoders should support both packed and repeated fields independent of whether
+ * a field is specified as packed in the schema. Check that decoding works with both types (packed and non-packed)
+ * if the data itself is not packed.
+ */
+ @Test
+ fun testDecodeNonPackedFloatArrayProtobuf() {
+ val obj: BaseFloatArrayCarrier = PackedFloatArrayCarrier(1234567890L.toULong(), floatArrayOf(1f, 2f, 3f))
+ val s = """10D285D8CC041D0000803F1D000000401D00004040"""
+ val decodedPacked = ProtoBuf.decodeFromHexString(PackedFloatArrayCarrier.serializer(), s)
+ assertEquals(obj, decodedPacked)
+ val decodedNonPacked = ProtoBuf.decodeFromHexString(NonPackedFloatArrayCarrier.serializer(), s)
+ assertEquals(obj, decodedNonPacked)
+ }
+
+ /**
+ * Test that serializing a list of strings is never packed, and deserialization ignores the packing annotation.
+ */
+ @Test
+ fun testEncodeAnnotatedStringList() {
+ val obj = PackedStringCarrier(listOf("aaa", "bbb", "ccc"))
+ val expectedHex = "020361616102036262620203636363"
+ val encodedHex = ProtoBuf.encodeToHexString(obj)
+ assertEquals(expectedHex, encodedHex)
+ assertEquals(obj, ProtoBuf.decodeFromHexString<PackedStringCarrier>(expectedHex))
+
+ val invalidPackedHex = "020C036161610362626203636363"
+ val decoded = ProtoBuf.decodeFromHexString<PackedStringCarrier>(invalidPackedHex)
+ val invalidString = "\u0003aaa\u0003bbb\u0003ccc"
+ assertEquals(PackedStringCarrier(listOf(invalidString)), decoded)
+ }
+
+ /**
+ * Test that toplevel "packed" lists with only byte length also work.
+ */
+ @Test
+ fun testDecodeToplevelPackedList() {
+ val input = "0feffdb6f507e6cc9933ba0180feff03"
+ val listData = listOf(0x7eadbeef, 0x6666666, 0xba, 0x7fff00)
+ val decoded = ProtoBuf.decodeFromHexString<List<Int>>(input)
+
+ assertEquals(listData, decoded)
+ }
+
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufAbsenceTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufAbsenceTest.kt
new file mode 100644
index 00000000..a37f84cb
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufAbsenceTest.kt
@@ -0,0 +1,152 @@
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.SerializationException
+import kotlinx.serialization.decodeFromByteArray
+import kotlinx.serialization.encodeToByteArray
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+
+class ProtobufAbsenceTest {
+ @Serializable
+ data class SimpleValue(val i: Int)
+ @Serializable
+ data class DefaultValue(val i: Int = 42)
+ @Serializable
+ data class NullableValue(val i: Int?)
+ @Serializable
+ data class DefaultAndNullValue(val i: Int? = 42)
+
+ @Serializable
+ data class SimpleList(val l: List<Int>)
+ @Serializable
+ data class DefaultList(val l: List<Int> = listOf(42))
+ @Serializable
+ data class NullableList(val l: List<Int>?)
+ @Serializable
+ data class DefaultNullableList(val l: List<Int>? = listOf(42))
+
+ @Serializable
+ data class SimpleMap(val m: Map<Int, Int>)
+ @Serializable
+ data class DefaultMap(val m: Map<Int, Int> = mapOf(42 to 43))
+ @Serializable
+ data class NullableMap(val m: Map<Int, Int>?)
+ @Serializable
+ data class DefaultNullableMap(val m: Map<Int, Int>? = mapOf(42 to 43))
+
+ @Test
+ fun testSimpleValue() {
+ assertFailsWith(SerializationException::class) { ProtoBuf.decodeFromByteArray<SimpleValue>(ByteArray(0)) }
+ }
+
+ @Test
+ fun testDefaultValue() {
+ val bytes = ProtoBuf.encodeToByteArray(DefaultValue(42))
+ assertEquals(0, bytes.size)
+
+ val decoded = ProtoBuf.decodeFromByteArray<DefaultValue>(bytes)
+ assertEquals(42, decoded.i)
+ }
+
+ @Test
+ fun testNullableValue() {
+ val bytes = ProtoBuf.encodeToByteArray(NullableValue(null))
+ assertEquals(0, bytes.size)
+
+ val decoded = ProtoBuf.decodeFromByteArray<NullableValue>(bytes)
+ assertEquals(null, decoded.i)
+ }
+
+ @Test
+ fun testDefaultAndNullValue() {
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(DefaultAndNullValue(null)) }
+
+ val bytes = ProtoBuf.encodeToByteArray(DefaultAndNullValue(42))
+ assertEquals(0, bytes.size)
+
+ val decoded = ProtoBuf.decodeFromByteArray<DefaultAndNullValue>(bytes)
+ assertEquals(42, decoded.i)
+ }
+
+
+ @Test
+ fun testSimpleList() {
+ val bytes = ProtoBuf.encodeToByteArray(SimpleList(emptyList()))
+ assertEquals(0, bytes.size)
+
+ val decoded = ProtoBuf.decodeFromByteArray<SimpleList>(bytes)
+ assertEquals(emptyList(), decoded.l)
+ }
+
+ @Test
+ fun testDefaultList() {
+ val bytes = ProtoBuf.encodeToByteArray(DefaultList(listOf(42)))
+ assertEquals(0, bytes.size)
+
+ val decoded = ProtoBuf.decodeFromByteArray<DefaultList>(bytes)
+ assertEquals(listOf(42), decoded.l)
+ }
+
+ @Test
+ fun testNullableList() {
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(NullableList(null)) }
+
+ val bytes = ProtoBuf.encodeToByteArray(NullableList(emptyList()))
+ assertEquals(0, bytes.size)
+
+ val decoded = ProtoBuf.decodeFromByteArray<NullableList>(bytes)
+ assertEquals(emptyList(), decoded.l)
+ }
+
+
+ @Test
+ fun testDefaultNullableList() {
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(DefaultNullableList(null)) }
+
+ val bytes = ProtoBuf.encodeToByteArray(DefaultNullableList(listOf(42)))
+ assertEquals(0, bytes.size)
+
+ val decoded = ProtoBuf.decodeFromByteArray<DefaultNullableList>(bytes)
+ assertEquals(listOf(42), decoded.l)
+ }
+
+ @Test
+ fun testSimpleMap() {
+ val bytes = ProtoBuf.encodeToByteArray(SimpleMap(emptyMap()))
+ assertEquals(0, bytes.size)
+ val decoded = ProtoBuf.decodeFromByteArray<SimpleMap>(bytes)
+ assertEquals(emptyMap(), decoded.m)
+ }
+
+ @Test
+ fun testDefaultMap() {
+ val bytes = ProtoBuf.encodeToByteArray(DefaultMap(mapOf(42 to 43)))
+ assertEquals(0, bytes.size)
+ val decoded = ProtoBuf.decodeFromByteArray<DefaultMap>(bytes)
+ assertEquals(mapOf(42 to 43), decoded.m)
+ }
+
+ @Test
+ fun testNullableMap() {
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(NullableMap(null)) }
+
+ val bytes = ProtoBuf.encodeToByteArray(NullableMap(emptyMap()))
+ assertEquals(0, bytes.size)
+
+ val decoded = ProtoBuf.decodeFromByteArray<NullableMap>(bytes)
+ assertEquals(emptyMap(), decoded.m)
+ }
+
+ @Test
+ fun testDefaultNullableMap() {
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(DefaultNullableMap(null)) }
+
+ val bytes = ProtoBuf.encodeToByteArray(DefaultNullableMap(mapOf(42 to 43)))
+ assertEquals(0, bytes.size)
+
+ val decoded = ProtoBuf.decodeFromByteArray<DefaultNullableMap>(bytes)
+ assertEquals(mapOf(42 to 43), decoded.m)
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufCollectionsTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufCollectionsTest.kt
new file mode 100644
index 00000000..43b667bf
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufCollectionsTest.kt
@@ -0,0 +1,104 @@
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.assertNotEquals
+
+class ProtobufCollectionsTest {
+ @Serializable
+ data class ListWithNestedList(val l: List<List<Int>?>)
+
+ @Serializable
+ data class ListWithNestedMap(val l: List<Map<Int, Int>>)
+
+ @Serializable
+ data class MapWithNullableNestedLists(val m: Map<List<Int>?, List<Int>?>)
+
+ @Serializable
+ data class NullableListElement(val l: List<Int?>)
+
+ @Serializable
+ data class NullableMapKey(val m: Map<Int?, Int>)
+
+ @Serializable
+ data class NullableMapValue(val m: Map<Int, Int?>)
+
+ @Test
+ fun testEncodeNullAsListElement() {
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(NullableListElement(listOf(null))) }
+ }
+
+ @Test
+ fun testEncodeNullAsMapKey() {
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(NullableMapKey(mapOf(null to 42))) }
+ }
+
+ @Test
+ fun testEmptyListIsNotListOfEmpty() {
+ val emptyListBytes = ProtoBuf.encodeToByteArray(ListWithNestedList(emptyList()))
+ val listOfEmptyBytes = ProtoBuf.encodeToByteArray(ListWithNestedList(listOf(emptyList())))
+ val emptyList = ProtoBuf.decodeFromByteArray<ListWithNestedList>(emptyListBytes)
+ val listOfEmpty = ProtoBuf.decodeFromByteArray<ListWithNestedList>(listOfEmptyBytes)
+
+ assertNotEquals(emptyList, listOfEmpty)
+ }
+
+ @Test
+ fun testEncodeMapWithNullableKey() {
+ val map = NullableMapKey(mapOf(42 to 43))
+ val bytes = ProtoBuf.encodeToByteArray(map)
+ val decoded = ProtoBuf.decodeFromByteArray<NullableMapKey>(bytes)
+ assertEquals(map, decoded)
+ }
+
+ @Test
+ fun testEncodeNullAsMapValue() {
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(NullableMapValue(mapOf(42 to null))) }
+ }
+
+ @Test
+ fun testEncodeMapWithNullableValue() {
+ val map = NullableMapValue(mapOf(42 to 43))
+ val bytes = ProtoBuf.encodeToByteArray(map)
+ val decoded = ProtoBuf.decodeFromByteArray<NullableMapValue>(bytes)
+ assertEquals(map, decoded)
+ }
+
+ @Test
+ fun testNestedList() {
+ val lists = listOf(listOf(42, 0), emptyList(), listOf(43))
+ val bytes = ProtoBuf.encodeToByteArray(ListWithNestedList(lists))
+ val decoded = ProtoBuf.decodeFromByteArray<ListWithNestedList>(bytes)
+ assertEquals(lists, decoded.l)
+ }
+
+ @Test
+ fun testNestedListIsNull() {
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(ListWithNestedList(listOf(null))) }
+ }
+
+ @Test
+ fun testNestedMapInList() {
+ val list = listOf(mapOf(1 to 2, 2 to 4), emptyMap(), mapOf(3 to 8))
+ val bytes = ProtoBuf.encodeToByteArray(ListWithNestedMap(list))
+ val decoded = ProtoBuf.decodeFromByteArray<ListWithNestedMap>(bytes)
+ assertEquals(list, decoded.l)
+ }
+
+ @Test
+ fun testNestedListsInMap() {
+ val map = mapOf<List<Int>?, List<Int>?>(listOf(42, 0) to listOf(43, 1), listOf(5) to listOf(20, 11))
+ val bytes = ProtoBuf.encodeToByteArray(MapWithNullableNestedLists(map))
+ val decoded = ProtoBuf.decodeFromByteArray<MapWithNullableNestedLists>(bytes)
+ assertEquals(map, decoded.m)
+ }
+
+ @Test
+ fun testNestedListsAreNullInMap() {
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(MapWithNullableNestedLists(mapOf(null to emptyList()))) }
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(MapWithNullableNestedLists(mapOf(emptyList<Int>() to null))) }
+ assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(MapWithNullableNestedLists(mapOf(null to null))) }
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufEnumTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufEnumTest.kt
new file mode 100644
index 00000000..5063daa9
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufEnumTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class ProtobufEnumTest {
+
+ enum class SomeEnum { ALPHA, BETA, GAMMA }
+
+ @Serializable
+ data class EnumWithUnion(@ProtoNumber(5) val s: String,
+ @ProtoNumber(6) val e: SomeEnum = SomeEnum.ALPHA,
+ @ProtoNumber(7) val i: Int = 42)
+
+ @Test
+ fun testEnumWithUnion() {
+ val data = EnumWithUnion("foo", SomeEnum.BETA)
+ val hex = ProtoBuf.encodeToHexString(EnumWithUnion.serializer(), data)
+ val restored = ProtoBuf.decodeFromHexString(EnumWithUnion.serializer(), hex)
+ assertEquals(data, restored)
+ }
+
+ @Serializable
+ class EnumHolder(val e: SomeEnum)
+
+ @Test
+ fun testUnknownValue() {
+ val bytes = ProtoBuf.encodeToByteArray(EnumHolder(SomeEnum.ALPHA))
+ bytes[1] = 3
+ assertFailsWith<SerializationException> { ProtoBuf.decodeFromByteArray<EnumHolder>(bytes) }
+ bytes[1] = -1
+ assertFailsWith<SerializationException> { ProtoBuf.decodeFromByteArray<EnumHolder>(bytes) }
+ }
+
+ @Serializable
+ enum class AnnotatedEnum {
+ @ProtoNumber(1) E1,
+ @ProtoNumber(4) E2
+ }
+
+ @Serializable
+ class Holder(val e: AnnotatedEnum)
+
+ @Test
+ fun testUnknownValueForAnnotatedEnum() {
+ val bytes = ProtoBuf.encodeToByteArray(Holder(AnnotatedEnum.E1))
+ bytes[1] = 3
+ assertFailsWith<SerializationException> { ProtoBuf.decodeFromByteArray<Holder>(bytes) }
+ bytes[1] = -1
+ assertFailsWith<SerializationException> { ProtoBuf.decodeFromByteArray<Holder>(bytes) }
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufHugeClassTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufHugeClassTest.kt
new file mode 100644
index 00000000..6f323e67
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufHugeClassTest.kt
@@ -0,0 +1,605 @@
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.HexConverter
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.decodeFromByteArray
+import kotlinx.serialization.encodeToByteArray
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ProtobufHugeClassTest {
+ @Serializable
+ data class Lists64(
+ val field0: List<Int>,
+ val field1: List<Int>,
+ val field2: List<Int>,
+ val field3: List<Int>,
+ val field4: List<Int>,
+ val field5: List<Int>,
+ val field6: List<Int>,
+ val field7: List<Int>,
+ val field8: List<Int>,
+ val field9: List<Int>,
+
+ val field10: List<Int>,
+ val field11: List<Int>,
+ val field12: List<Int>,
+ val field13: List<Int>,
+ val field14: List<Int>,
+ val field15: List<Int>,
+ val field16: List<Int>,
+ val field17: List<Int>,
+ val field18: List<Int>,
+ val field19: List<Int>,
+
+ val field20: List<Int>,
+ val field21: List<Int>,
+ val field22: List<Int>,
+ val field23: List<Int>,
+ val field24: List<Int>,
+ val field25: List<Int>,
+ val field26: List<Int>,
+ val field27: List<Int>,
+ val field28: List<Int>,
+ val field29: List<Int>,
+
+ val field30: List<Int>,
+ val field31: List<Int>,
+ val field32: List<Int>,
+ val field33: List<Int>,
+ val field34: List<Int>,
+ val field35: List<Int>,
+ val field36: List<Int>,
+ val field37: List<Int>,
+ val field38: List<Int>,
+ val field39: List<Int>,
+
+ val field40: List<Int>,
+ val field41: List<Int>,
+ val field42: List<Int>,
+ val field43: List<Int>,
+ val field44: List<Int>,
+ val field45: List<Int>,
+ val field46: List<Int>,
+ val field47: List<Int>,
+ val field48: List<Int>,
+ val field49: List<Int>,
+
+ val field50: List<Int>,
+ val field51: List<Int>,
+ val field52: List<Int>,
+ val field53: List<Int>,
+ val field54: List<Int>,
+ val field55: List<Int>,
+ val field56: List<Int>,
+ val field57: List<Int>,
+ val field58: List<Int>,
+ val field59: List<Int>,
+
+ val field60: List<Int>,
+ val field61: List<Int>,
+ val field62: List<Int>,
+ val field63: List<Int>
+ )
+
+ @Serializable
+ data class Values70(
+ val field0: Int?,
+ val field1: Int?,
+ val field2: Int?,
+ val field3: Int?,
+ val field4: Int?,
+ val field5: Int?,
+ val field6: Int?,
+ val field7: Int?,
+ val field8: Int?,
+ val field9: Int?,
+
+ val field10: Int?,
+ val field11: Int?,
+ val field12: Int?,
+ val field13: Int?,
+ val field14: Int?,
+ val field15: Int?,
+ val field16: Int?,
+ val field17: Int?,
+ val field18: Int?,
+ val field19: Int?,
+
+ val field20: Int?,
+ val field21: Int?,
+ val field22: Int?,
+ val field23: Int?,
+ val field24: Int?,
+ val field25: Int?,
+ val field26: Int?,
+ val field27: Int?,
+ val field28: Int?,
+ val field29: Int?,
+
+ val field30: Int?,
+ val field31: Int?,
+ val field32: Int?,
+ val field33: Int?,
+ val field34: Int?,
+ val field35: Int?,
+ val field36: Int?,
+ val field37: Int?,
+ val field38: Int?,
+ val field39: Int?,
+
+ val field40: Int?,
+ val field41: Int?,
+ val field42: Int?,
+ val field43: Int?,
+ val field44: Int?,
+ val field45: Int?,
+ val field46: Int?,
+ val field47: Int?,
+ val field48: Int?,
+ val field49: Int?,
+
+ val field50: Int?,
+ val field51: Int?,
+ val field52: Int?,
+ val field53: Int?,
+ val field54: Int?,
+ val field55: Int?,
+ val field56: Int?,
+ val field57: Int?,
+ val field58: Int?,
+ val field59: Int?,
+
+ val field60: Int?,
+ val field61: Int?,
+ val field62: Int?,
+ val field63: Int?,
+ val field64: Int?,
+ val field65: Int?,
+ val field66: Int?,
+ val field67: Int?,
+ val field68: Int?,
+ val field69: Int?
+ )
+
+ @Serializable
+ data class Values128(
+ val field0: Int?,
+ val field1: Int?,
+ val field2: Int?,
+ val field3: Int?,
+ val field4: Int?,
+ val field5: Int?,
+ val field6: Int?,
+ val field7: Int?,
+ val field8: Int?,
+ val field9: Int?,
+
+ val field10: Int?,
+ val field11: Int?,
+ val field12: Int?,
+ val field13: Int?,
+ val field14: Int?,
+ val field15: Int?,
+ val field16: Int?,
+ val field17: Int?,
+ val field18: Int?,
+ val field19: Int?,
+
+ val field20: Int?,
+ val field21: Int?,
+ val field22: Int?,
+ val field23: Int?,
+ val field24: Int?,
+ val field25: Int?,
+ val field26: Int?,
+ val field27: Int?,
+ val field28: Int?,
+ val field29: Int?,
+
+ val field30: Int?,
+ val field31: Int?,
+ val field32: Int?,
+ val field33: Int?,
+ val field34: Int?,
+ val field35: Int?,
+ val field36: Int?,
+ val field37: Int?,
+ val field38: Int?,
+ val field39: Int?,
+
+ val field40: Int?,
+ val field41: Int?,
+ val field42: Int?,
+ val field43: Int?,
+ val field44: Int?,
+ val field45: Int?,
+ val field46: Int?,
+ val field47: Int?,
+ val field48: Int?,
+ val field49: Int?,
+
+ val field50: Int?,
+ val field51: Int?,
+ val field52: Int?,
+ val field53: Int?,
+ val field54: Int?,
+ val field55: Int?,
+ val field56: Int?,
+ val field57: Int?,
+ val field58: Int?,
+ val field59: Int?,
+
+ val field60: Int?,
+ val field61: Int?,
+ val field62: Int?,
+ val field63: Int?,
+ val field64: Int?,
+ val field65: Int?,
+ val field66: Int?,
+ val field67: Int?,
+ val field68: Int?,
+ val field69: Int?,
+
+ val field70: Int?,
+ val field71: Int?,
+ val field72: Int?,
+ val field73: Int?,
+ val field74: Int?,
+ val field75: Int?,
+ val field76: Int?,
+ val field77: Int?,
+ val field78: Int?,
+ val field79: Int?,
+
+ val field80: Int?,
+ val field81: Int?,
+ val field82: Int?,
+ val field83: Int?,
+ val field84: Int?,
+ val field85: Int?,
+ val field86: Int?,
+ val field87: Int?,
+ val field88: Int?,
+ val field89: Int?,
+
+ val field90: Int?,
+ val field91: Int?,
+ val field92: Int?,
+ val field93: Int?,
+ val field94: Int?,
+ val field95: Int?,
+ val field96: Int?,
+ val field97: Int?,
+ val field98: Int?,
+ val field99: Int?,
+
+ val field100: Int?,
+ val field101: Int?,
+ val field102: Int?,
+ val field103: Int?,
+ val field104: Int?,
+ val field105: Int?,
+ val field106: Int?,
+ val field107: Int?,
+ val field108: Int?,
+ val field109: Int?,
+
+ val field110: Int?,
+ val field111: Int?,
+ val field112: Int?,
+ val field113: Int?,
+ val field114: Int?,
+ val field115: Int?,
+ val field116: Int?,
+ val field117: Int?,
+ val field118: Int?,
+ val field119: Int?,
+
+ val field120: Int?,
+ val field121: Int?,
+ val field122: Int?,
+ val field123: Int?,
+ val field124: Int?,
+ val field125: Int?,
+ val field126: Int?,
+ val field127: Int?
+ )
+ @Serializable
+ data class Values130(
+ val field0: Int?,
+ val field1: Int?,
+ val field2: Int?,
+ val field3: Int?,
+ val field4: Int?,
+ val field5: Int?,
+ val field6: Int?,
+ val field7: Int?,
+ val field8: Int?,
+ val field9: Int?,
+
+ val field10: Int?,
+ val field11: Int?,
+ val field12: Int?,
+ val field13: Int?,
+ val field14: Int?,
+ val field15: Int?,
+ val field16: Int?,
+ val field17: Int?,
+ val field18: Int?,
+ val field19: Int?,
+
+ val field20: Int?,
+ val field21: Int?,
+ val field22: Int?,
+ val field23: Int?,
+ val field24: Int?,
+ val field25: Int?,
+ val field26: Int?,
+ val field27: Int?,
+ val field28: Int?,
+ val field29: Int?,
+
+ val field30: Int?,
+ val field31: Int?,
+ val field32: Int?,
+ val field33: Int?,
+ val field34: Int?,
+ val field35: Int?,
+ val field36: Int?,
+ val field37: Int?,
+ val field38: Int?,
+ val field39: Int?,
+
+ val field40: Int?,
+ val field41: Int?,
+ val field42: Int?,
+ val field43: Int?,
+ val field44: Int?,
+ val field45: Int?,
+ val field46: Int?,
+ val field47: Int?,
+ val field48: Int?,
+ val field49: Int?,
+
+ val field50: Int?,
+ val field51: Int?,
+ val field52: Int?,
+ val field53: Int?,
+ val field54: Int?,
+ val field55: Int?,
+ val field56: Int?,
+ val field57: Int?,
+ val field58: Int?,
+ val field59: Int?,
+
+ val field60: Int?,
+ val field61: Int?,
+ val field62: Int?,
+ val field63: Int?,
+ val field64: Int?,
+ val field65: Int?,
+ val field66: Int?,
+ val field67: Int?,
+ val field68: Int?,
+ val field69: Int?,
+
+ val field70: Int?,
+ val field71: Int?,
+ val field72: Int?,
+ val field73: Int?,
+ val field74: Int?,
+ val field75: Int?,
+ val field76: Int?,
+ val field77: Int?,
+ val field78: Int?,
+ val field79: Int?,
+
+ val field80: Int?,
+ val field81: Int?,
+ val field82: Int?,
+ val field83: Int?,
+ val field84: Int?,
+ val field85: Int?,
+ val field86: Int?,
+ val field87: Int?,
+ val field88: Int?,
+ val field89: Int?,
+
+ val field90: Int?,
+ val field91: Int?,
+ val field92: Int?,
+ val field93: Int?,
+ val field94: Int?,
+ val field95: Int?,
+ val field96: Int?,
+ val field97: Int?,
+ val field98: Int?,
+ val field99: Int?,
+
+ val field100: Int?,
+ val field101: Int?,
+ val field102: Int?,
+ val field103: Int?,
+ val field104: Int?,
+ val field105: Int?,
+ val field106: Int?,
+ val field107: Int?,
+ val field108: Int?,
+ val field109: Int?,
+
+ val field110: Int?,
+ val field111: Int?,
+ val field112: Int?,
+ val field113: Int?,
+ val field114: Int?,
+ val field115: Int?,
+ val field116: Int?,
+ val field117: Int?,
+ val field118: Int?,
+ val field119: Int?,
+
+ val field120: Int?,
+ val field121: Int?,
+ val field122: Int?,
+ val field123: Int?,
+ val field124: Int?,
+ val field125: Int?,
+ val field126: Int?,
+ val field127: Int?,
+ val field128: Int?,
+ val field129: Int?
+ )
+
+ private val lists64: Lists64 =
+ Lists64(
+ emptyList(),
+ listOf(1, 42),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+
+ emptyList(),
+ emptyList(),
+ listOf(12, 43),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ emptyList()
+ )
+
+ private val values70: Values70 = Values70(
+ null, null, null, 3, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, 42, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, 66, null, null, null
+ )
+
+ private val values128: Values128 = Values128(
+ null, null, null, 3, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, 42, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, 66, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, 115, null, null, null, null,
+ null, null, null, null, null, null, null, null
+ )
+
+ private val values130: Values130 = Values130(
+ null, null, null, 3, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, 42, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, 66, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, 115, null, null, null, null,
+ null, null, null, null, null, null, null, null, 128, null
+ )
+
+ @Test
+ fun testLists64() {
+ val bytes = ProtoBuf.encodeToByteArray(lists64)
+ println(HexConverter.printHexBinary(bytes))
+
+ val decoded = ProtoBuf.decodeFromByteArray<Lists64>(bytes)
+ assertEquals(lists64, decoded)
+ }
+
+ @Test
+ fun testValues70() {
+ val bytes = ProtoBuf.encodeToByteArray(values70)
+ println(HexConverter.printHexBinary(bytes))
+
+ val decoded = ProtoBuf.decodeFromByteArray<Values70>(bytes)
+ assertEquals(values70, decoded)
+ }
+
+ @Test
+ fun testValues128() {
+ val bytes = ProtoBuf.encodeToByteArray(values128)
+ println(HexConverter.printHexBinary(bytes))
+
+ val decoded = ProtoBuf.decodeFromByteArray<Values128>(bytes)
+ assertEquals(values128, decoded)
+ }
+
+ @Test
+ fun testValues130() {
+ val bytes = ProtoBuf.encodeToByteArray(values130)
+ println(HexConverter.printHexBinary(bytes))
+
+ val decoded = ProtoBuf.decodeFromByteArray<Values130>(bytes)
+ assertEquals(values130, decoded)
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufMissingFieldsTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufMissingFieldsTest.kt
new file mode 100644
index 00000000..18616dfe
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufMissingFieldsTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+import kotlin.test.*
+
+class ProtobufMissingFieldsTest {
+
+ private val buffer = byteArrayOf(10, 30, 8, 11, 16, 2, 26, 3, 115, 112, 97, 26, 6, 115, 112, 97, 95, 101, 115, 26, 2, 101, 115, 26, 5, 101, 115, 95, 101, 115, 32, 1, 16, 25)
+
+ @Test
+ fun testDeserializeAllFields() {
+ val items = ProtoBuf.decodeFromByteArray(Items.serializer(), buffer)
+ assertEquals(25, items.pageSize)
+ assertFalse(items.nextPage)
+ assertEquals(1, items.items.size)
+ assertEquals(ItemPlatform.Android, items.items[0].platform)
+ assertEquals(11, items.items[0].id)
+ assertEquals(listOf("spa", "spa_es", "es", "es_es"), items.items[0].language)
+ assertEquals(ItemContext.Context1, items.items[0].context)
+ }
+
+ @Test
+ fun testDeserializeSomeTagsAreNotInSchema() {
+ val items = ProtoBuf.decodeFromByteArray(ItemsWithoutPageSize.serializer(), buffer)
+ assertFalse(items.nextPage)
+ assertEquals(1, items.items.size)
+ assertEquals(11, items.items[0].id)
+ assertEquals(listOf("spa", "spa_es", "es", "es_es"), items.items[0].language)
+ assertEquals(ItemContext.Context1, items.items[0].context)
+ }
+
+ enum class ItemPlatform {
+ Unknown,
+ iOS,
+ Android
+ }
+
+ enum class ItemContext {
+ Unknown,
+ Context1,
+ Context2
+ }
+
+ @Serializable
+ data class Items(
+ @ProtoNumber(1)
+ val items: List<Item> = emptyList(),
+ @ProtoNumber(2)
+ val pageSize: Int? = null,
+ @ProtoNumber(3)
+ val nextPage: Boolean = false
+ )
+
+ @Serializable
+ data class Item(
+ @ProtoNumber(1)
+ val id: Int,
+ @ProtoNumber(2) @Serializable(with = ItemPlatformSerializer::class)
+ val platform: ItemPlatform = ItemPlatform.Unknown,
+ @ProtoNumber(3)
+ val language: List<String> = emptyList(),
+ @ProtoNumber(4) @Serializable(with = ItemContextSerializer::class)
+ val context: ItemContext = ItemContext.Unknown
+ )
+
+ @Serializable
+ data class ItemsWithoutPageSize(
+ @ProtoNumber(1)
+ val items: List<ItemWithoutPlatform> = emptyList(),
+ @ProtoNumber(3)
+ val nextPage: Boolean = false
+ )
+
+ @Serializable
+ data class ItemWithoutPlatform(
+ @ProtoNumber(1)
+ val id: Int,
+ @ProtoNumber(3)
+ val language: List<String> = emptyList(),
+ @ProtoNumber(4) @Serializable(with = ItemContextSerializer::class)
+ val context: ItemContext = ItemContext.Unknown
+ )
+
+ class ItemPlatformSerializer : KSerializer<ItemPlatform> {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("ItemPlatform", SerialKind.ENUM) {
+ enumValues<ItemPlatform>().forEach {
+ element(it.name, buildSerialDescriptor("$serialName.${it.name}", StructureKind.OBJECT))
+ }
+ }
+
+ override fun deserialize(decoder: Decoder): ItemPlatform {
+ val index = decoder.decodeInt()
+ return ItemPlatform.values()[index]
+ }
+
+ override fun serialize(encoder: Encoder, value: ItemPlatform) {
+ encoder.encodeInt(value.ordinal)
+ }
+ }
+
+ class ItemContextSerializer : KSerializer<ItemContext> {
+
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("ItemContext", SerialKind.ENUM) {
+ enumValues<ItemContext>().forEach {
+ element(it.name, buildSerialDescriptor("$serialName.${it.name}", StructureKind.OBJECT))
+ }
+ }
+
+ override fun deserialize(decoder: Decoder): ItemContext {
+ val index = decoder.decodeInt()
+ return ItemContext.values()[index]
+ }
+
+ override fun serialize(encoder: Encoder, value: ItemContext) {
+ encoder.encodeInt(value.ordinal)
+ }
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufNullAndDefaultTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufNullAndDefaultTest.kt
new file mode 100644
index 00000000..88450aad
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufNullAndDefaultTest.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlinx.serialization.test.isJsLegacy
+import kotlin.test.*
+
+class ProtobufNullAndDefaultTest {
+ @Serializable
+ class ProtoWithNullDefault(val s: String? = null)
+
+ @Serializable
+ class ProtoWithNullDefaultAlways(@EncodeDefault val s: String? = null)
+
+ @Serializable
+ class ProtoWithNullDefaultNever(@EncodeDefault(EncodeDefault.Mode.NEVER) val s: String? = null)
+
+ @Test
+ fun testProtobufDropDefaults() {
+ val proto = ProtoBuf { encodeDefaults = false }
+ assertEquals(0, proto.encodeToByteArray(ProtoWithNullDefault()).size)
+ if (isJsLegacy()) return // because of @EncodeDefault
+ assertFailsWith<SerializationException> { proto.encodeToByteArray(ProtoWithNullDefaultAlways()) }
+ assertEquals(0, proto.encodeToByteArray(ProtoWithNullDefaultNever()).size)
+ }
+
+ @Test
+ fun testProtobufEncodeDefaults() {
+ val proto = ProtoBuf { encodeDefaults = true }
+ assertFailsWith<SerializationException> { proto.encodeToByteArray(ProtoWithNullDefault()) }
+ if (isJsLegacy()) return // because of @EncodeDefault
+ assertFailsWith<SerializationException> { proto.encodeToByteArray(ProtoWithNullDefaultAlways()) }
+ assertEquals(0, proto.encodeToByteArray(ProtoWithNullDefaultNever()).size)
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPolymorphismTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPolymorphismTest.kt
new file mode 100644
index 00000000..ff308e60
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPolymorphismTest.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class ProtobufPolymorphismTest {
+ @Test
+ fun testAbstract() {
+ val obj = PolyBox(SimpleStringInheritor("str", 133))
+ assertSerializedToBinaryAndRestored(obj, PolyBox.serializer(),
+ ProtoBuf { encodeDefaults = true; serializersModule = SimplePolymorphicModule })
+ }
+
+ @Test
+ fun testSealed() {
+ val obj = SealedBox(
+ listOf(
+ SimpleSealed.SubSealedB(33),
+ SimpleSealed.SubSealedA("str")
+ )
+ )
+ assertSerializedToBinaryAndRestored(obj, SealedBox.serializer(), ProtoBuf)
+ }
+
+ @Serializable
+ sealed class Single {
+ @Serializable
+ data class Impl(val i: Int) : Single()
+ }
+
+ @Test
+ fun testSingleSealedClass() {
+ val expected =
+ "0a436b6f746c696e782e73657269616c697a6174696f6e2e70726f746f6275662e50726f746f627566506f6c796d6f72706869736d546573742e53696e676c652e496d706c1202082a"
+ assertEquals(expected, ProtoBuf.encodeToHexString(Single.serializer(), Single.Impl(42)))
+ assertEquals(Single.Impl(42), ProtoBuf.decodeFromHexString(Single.serializer(), expected))
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitiveWrappersTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitiveWrappersTest.kt
new file mode 100644
index 00000000..58350760
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitiveWrappersTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+/*
+ * TODO improve these tests:
+ * * All primitive types
+ * * All nullable types
+ * * Built-in types (TBD)
+ * * Primitives nullability
+ */
+class ProtobufPrimitiveWrappersTest {
+
+ @Test
+ fun testSignedInteger() {
+ assertSerializedToBinaryAndRestored(TestInt(-150), TestInt.serializer(), ProtoBuf, hexResultToCheck = "08AB02")
+ }
+
+ @Test
+ fun testIntList() {
+ assertSerializedToBinaryAndRestored(
+ TestList(listOf(1, 2, 3)),
+ TestList.serializer(), ProtoBuf, hexResultToCheck = "080108020803"
+ )
+ }
+
+ @Test
+ fun testString() {
+ assertSerializedToBinaryAndRestored(
+ TestString("testing"),
+ TestString.serializer(), ProtoBuf, hexResultToCheck = "120774657374696E67"
+ )
+ }
+
+ @Test
+ fun testTwiceNested() {
+ assertSerializedToBinaryAndRestored(
+ TestInner(TestInt(-150)),
+ TestInner.serializer(), ProtoBuf, hexResultToCheck = "1A0308AB02"
+ )
+ }
+
+ @Test
+ fun testMixedTags() {
+ assertSerializedToBinaryAndRestored(
+ TestComplex(42, "testing"),
+ TestComplex.serializer(), ProtoBuf, hexResultToCheck = "D0022A120774657374696E67"
+ )
+ }
+
+ @Test
+ fun testDefaultPrimitiveValues() {
+ assertSerializedToBinaryAndRestored(TestInt(0), TestInt.serializer(), ProtoBuf, hexResultToCheck = "0800")
+ assertSerializedToBinaryAndRestored(TestList(listOf()), TestList.serializer(), ProtoBuf, hexResultToCheck = "")
+ assertSerializedToBinaryAndRestored(
+ TestString(""),
+ TestString.serializer(), ProtoBuf, hexResultToCheck = "1200"
+ )
+ }
+
+ @Test
+ fun testFixedIntWithLong() {
+ assertSerializedToBinaryAndRestored(
+ TestNumbers(100500, Long.MAX_VALUE),
+ TestNumbers.serializer(), ProtoBuf, hexResultToCheck = "0D9488010010FFFFFFFFFFFFFFFF7F"
+ )
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitivesTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitivesTest.kt
new file mode 100644
index 00000000..56c7bfab
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitivesTest.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlin.test.*
+
+class ProtobufPrimitivesTest {
+
+ private fun <T> testConversion(data: T, serializer: KSerializer<T>, expectedHexString: String) {
+ val string = ProtoBuf.encodeToHexString(serializer, data).uppercase()
+ assertEquals(expectedHexString, string)
+ assertEquals(data, ProtoBuf.decodeFromHexString(serializer, string))
+ }
+
+ @Test
+ fun testPrimitiveTypes() {
+ testConversion(true, Boolean.serializer(), "01")
+ testConversion('c', Char.serializer(), "63")
+ testConversion(1, Byte.serializer(), "01")
+ testConversion(1, Short.serializer(), "01")
+ testConversion(1, Int.serializer(), "01")
+ testConversion(1, Long.serializer(), "01")
+ testConversion(1f, Float.serializer(), "0000803F")
+ testConversion(1.0, Double.serializer(), "000000000000F03F")
+ testConversion("string", String.serializer(), "06737472696E67")
+ testConversion(Unit, Unit.serializer(), "")
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ScatteredArraysTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ScatteredArraysTest.kt
new file mode 100644
index 00000000..58fe762f
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ScatteredArraysTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class ScatteredArraysTest {
+ @Serializable
+ data class ListData(val data: List<String>, val separator: String)
+
+ @Serializable
+ data class ByteData(val data: ByteArray, val separator: String) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is ByteData) return false
+
+ if (!data.contentEquals(other.data)) return false
+ if (separator != other.separator) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = data.contentHashCode()
+ result = 31 * result + separator.hashCode()
+ return result
+ }
+ }
+
+ private fun prepareListTestData(): String {
+ // Concatenate two serialized representations
+ // Resulting bytes would be [1, 2, foo, 3, bar]
+ // Protobuf per spec must read it as ListData([1,2,3], bar)
+ val d1 = ListData(listOf("1", "2"), "foo")
+ val d2 = ListData(listOf("3"), "bar")
+ return ProtoBuf.encodeToHexString(ListData.serializer(), d1) +
+ ProtoBuf.encodeToHexString(ListData.serializer(), d2)
+ }
+
+ private fun prepareByteTestData(): String {
+ // Concatenate two serialized representations
+ // Resulting bytes would be [1, 2, foo, 3, bar]
+ // Protobuf per spec must read it as ByteData([1,2,3], bar)
+ val d1 = ByteData(byteArrayOf(1, 2), "foo")
+ val d2 = ByteData(byteArrayOf(3), "bar")
+ return ProtoBuf.encodeToHexString(ByteData.serializer(), d1) +
+ ProtoBuf.encodeToHexString(ByteData.serializer(), d2)
+ }
+
+ private fun <T> doTest(serializer: KSerializer<T>, testData: String, goldenValue: T) {
+ val parsed = ProtoBuf.decodeFromHexString(serializer, testData)
+ assertEquals(goldenValue, parsed)
+ }
+
+ @Test
+ fun testListData() =
+ doTest(ListData.serializer(), prepareListTestData(), ListData(listOf("1", "2", "3"), "bar"))
+
+ @Test
+ fun testByteData() =
+ doTest(ByteData.serializer(), prepareByteTestData(), ByteData(byteArrayOf(1, 2, 3), "bar"))
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/SerializableTestClasses.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/SerializableTestClasses.kt
new file mode 100644
index 00000000..91773980
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/SerializableTestClasses.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+
+@Serializable
+data class TestInt(@ProtoNumber(1) @ProtoType(ProtoIntegerType.SIGNED) val a: Int)
+
+@Serializable
+data class TestList(@ProtoNumber(1) val a: List<Int> = emptyList())
+
+@Serializable
+data class TestString(@ProtoNumber(2) val b: String)
+
+@Serializable
+data class TestInner(@ProtoNumber(3) val a: TestInt)
+
+@Serializable
+data class TestComplex(@ProtoNumber(42) val b: Int, @ProtoNumber(2) val c: String)
+
+@Serializable
+data class TestNumbers(@ProtoNumber(1) @ProtoType(ProtoIntegerType.FIXED) val a: Int, @ProtoNumber(2) val b: Long)
+
+@Serializable
+data class TestIntWithList(
+ @ProtoNumber(1) val s: Int,
+ @ProtoNumber(10) val l: List<Int>
+)
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/schema/SchemaValidationsTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/schema/SchemaValidationsTest.kt
new file mode 100644
index 00000000..b7332312
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/schema/SchemaValidationsTest.kt
@@ -0,0 +1,115 @@
+package kotlinx.serialization.protobuf.schema
+
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+import kotlin.test.Test
+import kotlin.test.assertContains
+import kotlin.test.assertFailsWith
+
+class SchemaValidationsTest {
+ @Serializable
+ data class ValidClass(val i: Int)
+
+ @Serializable
+ @SerialName("ValidClass")
+ data class DuplicateClass(val l: Long)
+
+ @Serializable
+ @SerialName("invalid serial name")
+ data class InvalidClassName(val i: Int)
+
+ @Serializable
+ data class InvalidClassFieldName(@SerialName("invalid serial name") val i: Int)
+
+ @Serializable
+ data class FieldNumberDuplicates(@ProtoNumber(42) val i: Int, @ProtoNumber(42) val j: Int)
+
+ @Serializable
+ data class FieldNumberImplicitlyDuplicates(@ProtoNumber(2) val i: Int, val j: Int)
+
+ @Serializable
+ @SerialName("invalid serial name")
+ enum class InvalidEnumName { SINGLETON }
+
+ @Serializable
+ enum class InvalidEnumElementName {
+ FIRST,
+
+ @SerialName("invalid serial name")
+ SECOND
+ }
+
+ @Test
+ fun testInvalidEnumElementSerialName() {
+ val descriptors = listOf(InvalidEnumElementName.serializer().descriptor)
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors) }
+ }
+
+ @Test
+ fun testInvalidClassSerialName() {
+ val descriptors = listOf(InvalidClassName.serializer().descriptor)
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors) }
+ }
+
+ @Test
+ fun testInvalidClassFieldSerialName() {
+ val descriptors = listOf(InvalidClassFieldName.serializer().descriptor)
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors) }
+ }
+
+ @Test
+ fun testDuplicateSerialNames() {
+ val descriptors = listOf(InvalidClassFieldName.serializer().descriptor)
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors) }
+ }
+
+ @Test
+ fun testInvalidEnumSerialName() {
+ val descriptors = listOf(InvalidEnumName.serializer().descriptor)
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors) }
+ }
+
+ @Test
+ fun testDuplicationSerialName() {
+ val descriptors = listOf(ValidClass.serializer().descriptor, DuplicateClass.serializer().descriptor)
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors) }
+ }
+
+ @Test
+ fun testInvalidOptionName() {
+ val descriptors = listOf(ValidClass.serializer().descriptor)
+ assertFailsWith(IllegalArgumentException::class) {
+ ProtoBufSchemaGenerator.generateSchemaText(
+ descriptors,
+ options = mapOf("broken name" to "value")
+ )
+ }
+ }
+
+ @Test
+ fun testIllegalPackageNames() {
+ val descriptors = listOf(ValidClass.serializer().descriptor)
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors, "") }
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors, ".") }
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors, ".first.dot") }
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors, "ended.with.dot.") }
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors, "first._underscore") }
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors, "first.1digit") }
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(descriptors, "illegal.sym+bol") }
+ }
+
+ @Test
+ fun testValidPackageNames() {
+ val descriptors = listOf(ValidClass.serializer().descriptor)
+ ProtoBufSchemaGenerator.generateSchemaText(descriptors, "singleIdent")
+ ProtoBufSchemaGenerator.generateSchemaText(descriptors, "double.ident")
+ ProtoBufSchemaGenerator.generateSchemaText(descriptors, "with.digits0123")
+ ProtoBufSchemaGenerator.generateSchemaText(descriptors, "with.underscore_")
+ }
+
+ @Test
+ fun testFieldNumberDuplicates() {
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(listOf(FieldNumberDuplicates.serializer().descriptor)) }
+ assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(listOf(FieldNumberImplicitlyDuplicates.serializer().descriptor)) }
+ }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt
new file mode 100644
index 00000000..c4a6b986
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+enum class Platform {
+ JVM, JS_LEGACY, JS_IR, NATIVE
+}
+
+public expect val currentPlatform: Platform
+
+public fun isJs(): Boolean = currentPlatform == Platform.JS_LEGACY || currentPlatform == Platform.JS_IR
+public fun isJsLegacy(): Boolean = currentPlatform == Platform.JS_LEGACY
+public fun isJvm(): Boolean = currentPlatform == Platform.JVM
+public fun isNative(): Boolean = currentPlatform == Platform.NATIVE
diff --git a/formats/protobuf/jsMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt b/formats/protobuf/jsMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt
new file mode 100644
index 00000000..72fbfd01
--- /dev/null
+++ b/formats/protobuf/jsMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf.internal
+
+private fun Short.reverseBytes(): Short = (((this.toInt() and 0xff) shl 8) or ((this.toInt() and 0xffff) ushr 8)).toShort()
+
+internal actual fun Int.reverseBytes(): Int =
+ ((this and 0xffff).toShort().reverseBytes().toInt() shl 16) or ((this ushr 16).toShort().reverseBytes().toInt() and 0xffff)
+
+internal actual fun Long.reverseBytes(): Long =
+ ((this and 0xffffffff).toInt().reverseBytes().toLong() shl 32) or ((this ushr 32).toInt()
+ .reverseBytes().toLong() and 0xffffffff)
diff --git a/formats/protobuf/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/formats/protobuf/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt
new file mode 100644
index 00000000..abbac9d0
--- /dev/null
+++ b/formats/protobuf/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+public actual val currentPlatform: Platform = if (isLegacyBackend()) Platform.JS_LEGACY else Platform.JS_IR
+
+// from https://github.com/JetBrains/kotlin/blob/569187a7516e2e5ab217158a3170d4beb0c5cb5a/js/js.translator/testData/_commonFiles/testUtils.kt#L3
+private fun isLegacyBackend(): Boolean =
+ // Using eval to prevent DCE from thinking that following code depends on Kotlin module.
+ eval("(typeof Kotlin != \"undefined\" && typeof Kotlin.kotlin != \"undefined\")").unsafeCast<Boolean>()
diff --git a/formats/protobuf/jvmMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt b/formats/protobuf/jvmMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt
new file mode 100644
index 00000000..121287f0
--- /dev/null
+++ b/formats/protobuf/jvmMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt
@@ -0,0 +1,9 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf.internal
+
+internal actual fun Int.reverseBytes(): Int = Integer.reverseBytes(this)
+
+internal actual fun Long.reverseBytes(): Long = java.lang.Long.reverseBytes(this)
diff --git a/formats/protobuf/jvmMainModule/src/module-info.java b/formats/protobuf/jvmMainModule/src/module-info.java
new file mode 100644
index 00000000..e0d1b32a
--- /dev/null
+++ b/formats/protobuf/jvmMainModule/src/module-info.java
@@ -0,0 +1,7 @@
+module kotlinx.serialization.protobuf {
+ requires transitive kotlin.stdlib;
+ requires transitive kotlinx.serialization.core;
+
+ exports kotlinx.serialization.protobuf;
+ exports kotlinx.serialization.protobuf.schema;
+}
diff --git a/formats/protobuf/jvmTest/resources/AbstractHolder.proto b/formats/protobuf/jvmTest/resources/AbstractHolder.proto
new file mode 100644
index 00000000..f753744e
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/AbstractHolder.proto
@@ -0,0 +1,14 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.AbstractHolder'
+message AbstractHolder {
+ required KotlinxSerializationPolymorphic abs = 1;
+}
+
+// This message was generated to support polymorphic types and does not present in Kotlin.
+message KotlinxSerializationPolymorphic {
+ required string type = 1;
+ required bytes value = 2;
+}
diff --git a/formats/protobuf/jvmTest/resources/ContextualHolder.proto b/formats/protobuf/jvmTest/resources/ContextualHolder.proto
new file mode 100644
index 00000000..5ea056ad
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/ContextualHolder.proto
@@ -0,0 +1,8 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.ContextualHolder'
+message ContextualHolder {
+ required bytes value = 1;
+}
diff --git a/formats/protobuf/jvmTest/resources/FieldNumberClass.proto b/formats/protobuf/jvmTest/resources/FieldNumberClass.proto
new file mode 100644
index 00000000..39ccb3a6
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/FieldNumberClass.proto
@@ -0,0 +1,10 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.FieldNumberClass'
+message FieldNumberClass {
+ required int32 a = 1;
+ required int32 b = 5;
+ required int32 c = 3;
+}
diff --git a/formats/protobuf/jvmTest/resources/LegacyMapHolder.proto b/formats/protobuf/jvmTest/resources/LegacyMapHolder.proto
new file mode 100644
index 00000000..4fa50be5
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/LegacyMapHolder.proto
@@ -0,0 +1,71 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.LegacyMapHolder'
+message LegacyMapHolder {
+ repeated LegacyMapHolder_keyAsMessage keyAsMessage = 1;
+ repeated LegacyMapHolder_keyAsEnum keyAsEnum = 2;
+ repeated LegacyMapHolder_keyAsBytes keyAsBytes = 3;
+ repeated LegacyMapHolder_keyAsList keyAsList = 4;
+ repeated LegacyMapHolder_keyAsDeepList keyAsDeepList = 5;
+ repeated LegacyMapHolder_nullableKeyAndValue nullableKeyAndValue = 6;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsMessage'
+message LegacyMapHolder_keyAsMessage {
+ required OptionsClass key = 1;
+ required int32 value = 2;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsEnum'
+message LegacyMapHolder_keyAsEnum {
+ required OverriddenEnumName key = 1;
+ required OptionsClass value = 2;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsBytes'
+message LegacyMapHolder_keyAsBytes {
+ required bytes key = 1;
+ required bytes value = 2;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsList'
+message LegacyMapHolder_keyAsList {
+ repeated int32 key = 1;
+ required bytes value = 2;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsDeepList'
+message LegacyMapHolder_keyAsDeepList {
+ repeated LegacyMapHolder_keyAsDeepList_key key = 1;
+ required bytes value = 2;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'nullableKeyAndValue'
+message LegacyMapHolder_nullableKeyAndValue {
+ required OptionsClass key = 1;
+ required OptionsClass value = 2;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.OptionsClass'
+message OptionsClass {
+ required int32 i = 1;
+}
+
+enum OverriddenEnumName {
+ FIRST = 0;
+ OverriddenElementName = 1;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsDeepList'
+message LegacyMapHolder_keyAsDeepList_key {
+ repeated int32 value = 1;
+}
diff --git a/formats/protobuf/jvmTest/resources/ListClass.proto b/formats/protobuf/jvmTest/resources/ListClass.proto
new file mode 100644
index 00000000..3867d865
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/ListClass.proto
@@ -0,0 +1,23 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.ListClass'
+message ListClass {
+ repeated int32 intList = 1;
+ repeated int32 intArray = 2;
+ // WARNING: nullable elements of collections can not be represented in protobuf
+ repeated int32 boxedIntArray = 3;
+ repeated OptionsClass messageList = 4;
+ repeated OverriddenEnumName enumList = 5;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.OptionsClass'
+message OptionsClass {
+ required int32 i = 1;
+}
+
+enum OverriddenEnumName {
+ FIRST = 0;
+ OverriddenElementName = 1;
+}
diff --git a/formats/protobuf/jvmTest/resources/MapClass.proto b/formats/protobuf/jvmTest/resources/MapClass.proto
new file mode 100644
index 00000000..d4e5a0c1
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/MapClass.proto
@@ -0,0 +1,21 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.MapClass'
+message MapClass {
+ map<int32, float> scalarMap = 1;
+ map<int32, bytes> bytesMap = 2;
+ map<string, OptionsClass> messageMap = 3;
+ map<bool, OverriddenEnumName> enumMap = 4;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.OptionsClass'
+message OptionsClass {
+ required int32 i = 1;
+}
+
+enum OverriddenEnumName {
+ FIRST = 0;
+ OverriddenElementName = 1;
+}
diff --git a/formats/protobuf/jvmTest/resources/NestedCollections.proto b/formats/protobuf/jvmTest/resources/NestedCollections.proto
new file mode 100644
index 00000000..a9a72337
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/NestedCollections.proto
@@ -0,0 +1,40 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.NestedCollections'
+message NestedCollections {
+ repeated NestedCollections_intList intList = 1;
+ repeated NestedCollections_messageList messageList = 2;
+ repeated NestedCollections_mapInList mapInList = 3;
+ map<string, NestedCollections_listInMap> listInMap = 4;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'NestedCollections', field 'intList'
+message NestedCollections_intList {
+ repeated int32 value = 1;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'NestedCollections', field 'messageList'
+message NestedCollections_messageList {
+ repeated OptionsClass value = 1;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'NestedCollections', field 'mapInList'
+message NestedCollections_mapInList {
+ map<string, OptionsClass> value = 1;
+}
+
+// This message was generated to support nested collection in map value and does not present in Kotlin.
+// Containing message 'NestedCollections', field 'listInMap'
+message NestedCollections_listInMap {
+ repeated int32 value = 1;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.OptionsClass'
+message OptionsClass {
+ required int32 i = 1;
+}
diff --git a/formats/protobuf/jvmTest/resources/NullableNestedCollections.proto b/formats/protobuf/jvmTest/resources/NullableNestedCollections.proto
new file mode 100644
index 00000000..462de242
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/NullableNestedCollections.proto
@@ -0,0 +1,46 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.NullableNestedCollections'
+message NullableNestedCollections {
+ repeated NullableNestedCollections_nullableIntList nullableIntList = 1;
+ // WARNING: nullable map values can not be represented in protobuf
+ map<string, NullableNestedCollections_nullableIntMap> nullableIntMap = 2;
+ map<string, NullableNestedCollections_intMap> intMap = 3;
+ repeated NullableNestedCollections_intList intList = 4;
+ repeated NullableNestedCollections_legacyMap legacyMap = 5;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'NullableNestedCollections', field 'nullableIntList'
+message NullableNestedCollections_nullableIntList {
+ repeated int32 value = 1;
+}
+
+// This message was generated to support nested collection in map value and does not present in Kotlin.
+// Containing message 'NullableNestedCollections', field 'nullableIntMap'
+message NullableNestedCollections_nullableIntMap {
+ repeated int32 value = 1;
+}
+
+// This message was generated to support nested collection in map value and does not present in Kotlin.
+// Containing message 'NullableNestedCollections', field 'intMap'
+message NullableNestedCollections_intMap {
+ // WARNING: nullable elements of collections can not be represented in protobuf
+ repeated int32 value = 1;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'NullableNestedCollections', field 'intList'
+message NullableNestedCollections_intList {
+ // WARNING: nullable elements of collections can not be represented in protobuf
+ repeated int32 value = 1;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'NullableNestedCollections', field 'legacyMap'
+message NullableNestedCollections_legacyMap {
+ repeated int32 key = 1;
+ repeated int32 value = 2;
+}
diff --git a/formats/protobuf/jvmTest/resources/OptionalClass.proto b/formats/protobuf/jvmTest/resources/OptionalClass.proto
new file mode 100644
index 00000000..41fdba71
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/OptionalClass.proto
@@ -0,0 +1,13 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.OptionalClass'
+message OptionalClass {
+ required int32 requiredInt = 1;
+ // WARNING: a default value decoded when value is missing
+ optional int32 optionalInt = 2;
+ optional int32 nullableInt = 3;
+ // WARNING: a default value decoded when value is missing
+ optional int32 nullableOptionalInt = 4;
+}
diff --git a/formats/protobuf/jvmTest/resources/OptionalCollections.proto b/formats/protobuf/jvmTest/resources/OptionalCollections.proto
new file mode 100644
index 00000000..58adfe38
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/OptionalCollections.proto
@@ -0,0 +1,21 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.OptionalCollections'
+message OptionalCollections {
+ repeated int32 requiredList = 1;
+ // WARNING: a default value decoded when value is missing
+ repeated int32 optionalList = 2;
+ // WARNING: an empty collection decoded when a value is missing
+ repeated int32 nullableList = 3;
+ // WARNING: a default value decoded when value is missing
+ repeated int32 nullableOptionalList = 4;
+ map<int32, int32> requiredMap = 5;
+ // WARNING: a default value decoded when value is missing
+ map<int32, int32> optionalMap = 6;
+ // WARNING: an empty collection decoded when a value is missing
+ map<int32, int32> nullableMap = 7;
+ // WARNING: a default value decoded when value is missing
+ map<int32, int32> nullableOptionalMap = 8;
+}
diff --git a/formats/protobuf/jvmTest/resources/OptionsClass.proto b/formats/protobuf/jvmTest/resources/OptionsClass.proto
new file mode 100644
index 00000000..2d308135
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/OptionsClass.proto
@@ -0,0 +1,10 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+option java_package = "api.proto";
+option java_outer_classname = "Outer";
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.OptionsClass'
+message OptionsClass {
+ required int32 i = 1;
+}
diff --git a/formats/protobuf/jvmTest/resources/PackedListClass.proto b/formats/protobuf/jvmTest/resources/PackedListClass.proto
new file mode 100644
index 00000000..bf096a16
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/PackedListClass.proto
@@ -0,0 +1,23 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.PackedListClass'
+message PackedListClass {
+ repeated int32 intList = 1 [packed=true];
+ repeated int32 intArray = 2 [packed=true];
+ // WARNING: nullable elements of collections can not be represented in protobuf
+ repeated int32 boxedIntArray = 3 [packed=true];
+ repeated OptionsClass messageList = 4;
+ repeated OverriddenEnumName enumList = 5;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.OptionsClass'
+message OptionsClass {
+ required int32 i = 1;
+}
+
+enum OverriddenEnumName {
+ FIRST = 0;
+ OverriddenElementName = 1;
+}
diff --git a/formats/protobuf/jvmTest/resources/ScalarHolder.proto b/formats/protobuf/jvmTest/resources/ScalarHolder.proto
new file mode 100644
index 00000000..8a45f50a
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/ScalarHolder.proto
@@ -0,0 +1,21 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.ScalarHolder'
+message ScalarHolder {
+ required int32 int = 1;
+ required sint32 intSigned = 2;
+ required fixed32 intFixed = 3;
+ required int32 intDefault = 4;
+ required int64 long = 5;
+ required sint64 longSigned = 6;
+ required fixed64 longFixed = 7;
+ required int32 longDefault = 8;
+ required bool flag = 9;
+ required bytes byteArray = 10;
+ required bytes boxedByteArray = 11;
+ required string text = 12;
+ required float float = 13;
+ required double double = 14;
+}
diff --git a/formats/protobuf/jvmTest/resources/SealedHolder.proto b/formats/protobuf/jvmTest/resources/SealedHolder.proto
new file mode 100644
index 00000000..a521bc5d
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/SealedHolder.proto
@@ -0,0 +1,27 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedHolder'
+message SealedHolder {
+ required SealedClass sealed = 1;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedClass'
+message SealedClass {
+ required string type = 1;
+ // decoded as message with one of these types:
+ // message Impl1, serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedClass.Impl1'
+ // message Impl2, serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedClass.Impl2'
+ required bytes value = 2;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedClass.Impl1'
+message Impl1 {
+ required int32 int = 1;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedClass.Impl2'
+message Impl2 {
+ required int64 long = 1;
+}
diff --git a/formats/protobuf/jvmTest/resources/SerialNameClass.proto b/formats/protobuf/jvmTest/resources/SerialNameClass.proto
new file mode 100644
index 00000000..622a16aa
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/SerialNameClass.proto
@@ -0,0 +1,13 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+message OverriddenClassName {
+ required int32 original = 1;
+ required OverriddenEnumName OverriddenFieldName = 2;
+}
+
+enum OverriddenEnumName {
+ FIRST = 0;
+ OverriddenElementName = 1;
+}
diff --git a/formats/protobuf/jvmTest/resources/common/schema.proto b/formats/protobuf/jvmTest/resources/common/schema.proto
new file mode 100644
index 00000000..e8a0b4cd
--- /dev/null
+++ b/formats/protobuf/jvmTest/resources/common/schema.proto
@@ -0,0 +1,272 @@
+syntax = "proto2";
+
+package kotlinx.serialization.protobuf.schema.generator;
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.ScalarHolder'
+message ScalarHolder {
+ required int32 int = 1;
+ required sint32 intSigned = 2;
+ required fixed32 intFixed = 3;
+ required int32 intDefault = 4;
+ required int64 long = 5;
+ required sint64 longSigned = 6;
+ required fixed64 longFixed = 7;
+ required int32 longDefault = 8;
+ required bool flag = 9;
+ required bytes byteArray = 10;
+ required bytes boxedByteArray = 11;
+ required string text = 12;
+ required float float = 13;
+ required double double = 14;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.FieldNumberClass'
+message FieldNumberClass {
+ required int32 a = 1;
+ required int32 b = 5;
+ required int32 c = 3;
+}
+
+message OverriddenClassName {
+ required int32 original = 1;
+ required OverriddenEnumName OverriddenFieldName = 2;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.ListClass'
+message ListClass {
+ repeated int32 intList = 1;
+ repeated int32 intArray = 2;
+ // WARNING: nullable elements of collections can not be represented in protobuf
+ repeated int32 boxedIntArray = 3;
+ repeated OptionsClass messageList = 4;
+ repeated OverriddenEnumName enumList = 5;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.PackedListClass'
+message PackedListClass {
+ repeated int32 intList = 1 [packed=true];
+ repeated int32 intArray = 2 [packed=true];
+ // WARNING: nullable elements of collections can not be represented in protobuf
+ repeated int32 boxedIntArray = 3 [packed=true];
+ repeated OptionsClass messageList = 4;
+ repeated OverriddenEnumName enumList = 5;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.MapClass'
+message MapClass {
+ map<int32, float> scalarMap = 1;
+ map<int32, bytes> bytesMap = 2;
+ map<string, OptionsClass> messageMap = 3;
+ map<bool, OverriddenEnumName> enumMap = 4;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.OptionalClass'
+message OptionalClass {
+ required int32 requiredInt = 1;
+ // WARNING: a default value decoded when value is missing
+ optional int32 optionalInt = 2;
+ optional int32 nullableInt = 3;
+ // WARNING: a default value decoded when value is missing
+ optional int32 nullableOptionalInt = 4;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.ContextualHolder'
+message ContextualHolder {
+ required bytes value = 1;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.AbstractHolder'
+message AbstractHolder {
+ required KotlinxSerializationPolymorphic abs = 1;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedHolder'
+message SealedHolder {
+ required SealedClass sealed = 1;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.NestedCollections'
+message NestedCollections {
+ repeated NestedCollections_intList intList = 1;
+ repeated NestedCollections_messageList messageList = 2;
+ repeated NestedCollections_mapInList mapInList = 3;
+ map<string, NestedCollections_listInMap> listInMap = 4;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.LegacyMapHolder'
+message LegacyMapHolder {
+ repeated LegacyMapHolder_keyAsMessage keyAsMessage = 1;
+ repeated LegacyMapHolder_keyAsEnum keyAsEnum = 2;
+ repeated LegacyMapHolder_keyAsBytes keyAsBytes = 3;
+ repeated LegacyMapHolder_keyAsList keyAsList = 4;
+ repeated LegacyMapHolder_keyAsDeepList keyAsDeepList = 5;
+ repeated LegacyMapHolder_nullableKeyAndValue nullableKeyAndValue = 6;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.NullableNestedCollections'
+message NullableNestedCollections {
+ repeated NullableNestedCollections_nullableIntList nullableIntList = 1;
+ // WARNING: nullable map values can not be represented in protobuf
+ map<string, NullableNestedCollections_nullableIntMap> nullableIntMap = 2;
+ map<string, NullableNestedCollections_intMap> intMap = 3;
+ repeated NullableNestedCollections_intList intList = 4;
+ repeated NullableNestedCollections_legacyMap legacyMap = 5;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.OptionalCollections'
+message OptionalCollections {
+ repeated int32 requiredList = 1;
+ // WARNING: a default value decoded when value is missing
+ repeated int32 optionalList = 2;
+ // WARNING: an empty collection decoded when a value is missing
+ repeated int32 nullableList = 3;
+ // WARNING: a default value decoded when value is missing
+ repeated int32 nullableOptionalList = 4;
+ map<int32, int32> requiredMap = 5;
+ // WARNING: a default value decoded when value is missing
+ map<int32, int32> optionalMap = 6;
+ // WARNING: an empty collection decoded when a value is missing
+ map<int32, int32> nullableMap = 7;
+ // WARNING: a default value decoded when value is missing
+ map<int32, int32> nullableOptionalMap = 8;
+}
+
+enum OverriddenEnumName {
+ FIRST = 0;
+ OverriddenElementName = 1;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.OptionsClass'
+message OptionsClass {
+ required int32 i = 1;
+}
+
+// This message was generated to support polymorphic types and does not present in Kotlin.
+message KotlinxSerializationPolymorphic {
+ required string type = 1;
+ required bytes value = 2;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedClass'
+message SealedClass {
+ required string type = 1;
+ // decoded as message with one of these types:
+ // message Impl1, serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedClass.Impl1'
+ // message Impl2, serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedClass.Impl2'
+ required bytes value = 2;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'NestedCollections', field 'intList'
+message NestedCollections_intList {
+ repeated int32 value = 1;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'NestedCollections', field 'messageList'
+message NestedCollections_messageList {
+ repeated OptionsClass value = 1;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'NestedCollections', field 'mapInList'
+message NestedCollections_mapInList {
+ map<string, OptionsClass> value = 1;
+}
+
+// This message was generated to support nested collection in map value and does not present in Kotlin.
+// Containing message 'NestedCollections', field 'listInMap'
+message NestedCollections_listInMap {
+ repeated int32 value = 1;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsMessage'
+message LegacyMapHolder_keyAsMessage {
+ required OptionsClass key = 1;
+ required int32 value = 2;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsEnum'
+message LegacyMapHolder_keyAsEnum {
+ required OverriddenEnumName key = 1;
+ required OptionsClass value = 2;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsBytes'
+message LegacyMapHolder_keyAsBytes {
+ required bytes key = 1;
+ required bytes value = 2;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsList'
+message LegacyMapHolder_keyAsList {
+ repeated int32 key = 1;
+ required bytes value = 2;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsDeepList'
+message LegacyMapHolder_keyAsDeepList {
+ repeated LegacyMapHolder_keyAsDeepList_key key = 1;
+ required bytes value = 2;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'nullableKeyAndValue'
+message LegacyMapHolder_nullableKeyAndValue {
+ required OptionsClass key = 1;
+ required OptionsClass value = 2;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'NullableNestedCollections', field 'nullableIntList'
+message NullableNestedCollections_nullableIntList {
+ repeated int32 value = 1;
+}
+
+// This message was generated to support nested collection in map value and does not present in Kotlin.
+// Containing message 'NullableNestedCollections', field 'nullableIntMap'
+message NullableNestedCollections_nullableIntMap {
+ repeated int32 value = 1;
+}
+
+// This message was generated to support nested collection in map value and does not present in Kotlin.
+// Containing message 'NullableNestedCollections', field 'intMap'
+message NullableNestedCollections_intMap {
+ // WARNING: nullable elements of collections can not be represented in protobuf
+ repeated int32 value = 1;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'NullableNestedCollections', field 'intList'
+message NullableNestedCollections_intList {
+ // WARNING: nullable elements of collections can not be represented in protobuf
+ repeated int32 value = 1;
+}
+
+// This message was generated to support legacy map and does not present in Kotlin.
+// Containing message 'NullableNestedCollections', field 'legacyMap'
+message NullableNestedCollections_legacyMap {
+ repeated int32 key = 1;
+ repeated int32 value = 2;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedClass.Impl1'
+message Impl1 {
+ required int32 int = 1;
+}
+
+// serial name 'kotlinx.serialization.protobuf.schema.GenerationTest.SealedClass.Impl2'
+message Impl2 {
+ required int64 long = 1;
+}
+
+// This message was generated to support nested collection in list and does not present in Kotlin.
+// Containing message 'LegacyMapHolder', field 'keyAsDeepList'
+message LegacyMapHolder_keyAsDeepList_key {
+ repeated int32 value = 1;
+}
diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/FormatConverterHelpers.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/FormatConverterHelpers.kt
new file mode 100644
index 00000000..d9125687
--- /dev/null
+++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/FormatConverterHelpers.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import com.google.protobuf.*
+import kotlinx.serialization.*
+import java.io.*
+
+interface IMessage {
+ fun toProtobufMessage(): GeneratedMessageV3
+}
+
+fun GeneratedMessageV3.toHex(): String {
+ val b = ByteArrayOutputStream()
+ this.writeTo(b)
+ return (HexConverter.printHexBinary(b.toByteArray(), lowerCase = true))
+}
+
+val defaultProtobuf = ProtoBuf { encodeDefaults = true }
+/**
+ * Check serialization of [ProtoBuf].
+ *
+ * 1. Serializes the given [IMessage] into bytes using [ProtoBuf].
+ * 2. Parses those bytes via the `Java ProtoBuf library`.
+ * 3. Compares parsed `Java ProtoBuf object` to expected object ([IMessage.toProtobufMessage]).
+ *
+ * @param it The [IMessage] to check.
+ * @param protoBuf Provide custom [ProtoBuf] instance (default: [ProtoBuf.plain]).
+ *
+ * @return `true` if the de-serialization returns the expected object.
+ */
+inline fun <reified T : IMessage> dumpCompare(it: T, alwaysPrint: Boolean = false, protoBuf: BinaryFormat = defaultProtobuf): Boolean {
+ val msg = it.toProtobufMessage()
+ var parsed: GeneratedMessageV3?
+ val c = try {
+ val bytes = protoBuf.encodeToByteArray(it)
+ if (alwaysPrint) println("Serialized bytes: ${HexConverter.printHexBinary(bytes)}")
+ parsed = msg.parserForType.parseFrom(bytes)
+ msg == parsed
+ } catch (e: Exception) {
+ e.printStackTrace()
+ parsed = null
+ false
+ }
+ if (!c || alwaysPrint) println("Expected: $msg\nfound: $parsed")
+ return c
+}
+
+/**
+ * Check de-serialization of [ProtoBuf].
+ *
+ * 1. Converts expected `Java ProtoBuf object` ([IMessage.toProtobufMessage]) to bytes.
+ * 2. Parses those bytes via [ProtoBuf].
+ * 3. Compares parsed ProtoBuf object to given object.
+ *
+ * @param it The [IMessage] to check.
+ * @param alwaysPrint Set to `true` if expected/found objects should always get printed to console (default: `false`).
+ * @param protoBuf Provide custom [ProtoBuf] instance (default: [ProtoBuf.plain]).
+ *
+ * @return `true` if the de-serialization returns the original object.
+ */
+inline fun <reified T : IMessage> readCompare(it: T, alwaysPrint: Boolean = false, protoBuf: BinaryFormat = defaultProtobuf): Boolean {
+ var obj: T?
+ val c = try {
+ val msg = it.toProtobufMessage()
+ val hex = msg.toHex()
+ obj = protoBuf.decodeFromHexString<T>(hex)
+ obj == it
+ } catch (e: Exception) {
+ obj = null
+ e.printStackTrace()
+ false
+ }
+ if (!c || alwaysPrint) println("Expected: $it\nfound: $obj")
+ return c
+}
+
diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/PolymorphicWithJvmClassTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/PolymorphicWithJvmClassTest.kt
new file mode 100644
index 00000000..df1a3fd1
--- /dev/null
+++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/PolymorphicWithJvmClassTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import org.junit.Test
+import java.text.*
+import java.util.*
+import kotlin.test.*
+
+class PolymorphicWithJvmClassTest {
+ @Serializable
+ data class DateWrapper(@ProtoNumber(1) @Polymorphic val date: Date)
+
+ @Serializer(forClass = Date::class)
+ object DateSerializer : KSerializer<Date> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("java.util.Date", PrimitiveKind.STRING)
+
+ // Consider wrapping in ThreadLocal if serialization may happen in multiple threads
+ private val df: DateFormat = SimpleDateFormat("dd/MM/yyyy HH:mm:ss.SSS").apply {
+ timeZone = TimeZone.getTimeZone("GMT+2")
+ }
+
+ override fun serialize(encoder: Encoder, value: Date) {
+ encoder.encodeString(df.format(value))
+ }
+
+ override fun deserialize(decoder: Decoder): Date {
+ return df.parse(decoder.decodeString())
+ }
+ }
+
+ @Test
+ fun testPolymorphicWrappedOverride() {
+ val protobuf = ProtoBuf { serializersModule = SerializersModule { polymorphic(Date::class, DateSerializer) } }
+ val obj = DateWrapper(Date())
+ val bytes = protobuf.encodeToHexString(obj)
+ val restored = protobuf.decodeFromHexString<DateWrapper>(bytes)
+ assertEquals(obj, restored)
+ }
+}
diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoBufNullTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoBufNullTest.kt
new file mode 100644
index 00000000..d2258f47
--- /dev/null
+++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoBufNullTest.kt
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+
+import org.junit.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+/**
+ * Test that [ProtoBuf] works correctly if [ProtoBuf.encodeDefaults] is set to `false`
+ * and that `null` is allowed as a default value.
+ *
+ * In this case `null` values should not get encoded into bytes. This allows to check if an optional
+ * field was set or not (like it is possible with the *Java ProtoBuf library*).
+ */
+class ProtoBufNullTest {
+
+ /** ProtoBuf instance that does **not** encode defaults. */
+ private val protoBuf = ProtoBuf { encodeDefaults = false }
+
+ @Test
+ fun testReadCompareWithNulls() {
+ val data = MessageWithOptionals()
+ assertTrue(readCompare(data, alwaysPrint = true, protoBuf = protoBuf))
+ }
+
+ @Test
+ fun testDumpCompareWithNulls() {
+ val data = MessageWithOptionals()
+ assertTrue(dumpCompare(data, alwaysPrint = true, protoBuf = protoBuf))
+ }
+
+ @Test
+ fun testReadCompareWithDefaults() {
+ val data = MessageWithOptionals(0, "", MessageWithOptionals.Position.FIRST, 99, listOf(1, 2, 3))
+ assertTrue(readCompare(data, alwaysPrint = true, protoBuf = protoBuf))
+ }
+
+ @Test
+ fun testDumpCompareWithDefaults() {
+ val data = MessageWithOptionals(0, "", MessageWithOptionals.Position.FIRST, 99, listOf(1, 2, 3))
+ assertTrue(dumpCompare(data, protoBuf = protoBuf))
+ }
+
+ @Test
+ fun testReadCompareWithValues() {
+ val data = MessageWithOptionals(42, "Test", MessageWithOptionals.Position.SECOND, 24, listOf(1, 2, 3))
+ assertTrue(readCompare(data, alwaysPrint = true, protoBuf = protoBuf))
+ }
+
+ @Test
+ fun testDumpCompareWithValues() {
+ val data = MessageWithOptionals(42, "Test", MessageWithOptionals.Position.SECOND, 24, listOf(1, 2, 3))
+ assertTrue(dumpCompare(data, alwaysPrint = true, protoBuf = protoBuf))
+ }
+
+ @Test
+ fun testThatNullValuesAreNotEncoded() {
+ val data = MessageWithOptionals()
+ val parsed = TestData.MessageWithOptionals.parseFrom(protoBuf.encodeToByteArray(data))
+
+ assertFalse(parsed.hasA(), "Expected that null value for optional field `a` is not encoded.")
+ assertFalse(parsed.hasB(), "Expected that null value for optional field `b` is not encoded.")
+ assertFalse(parsed.hasC(), "Expected that null value for optional field `c` is not encoded.")
+ assertFalse(parsed.hasD(), "Expected that null value for optional field `d` is not encoded.")
+
+ assertEquals(0, parsed.a, "Expected default value for field `a`.")
+ assertEquals("", parsed.b, "Expected default value for field `b`.")
+ assertEquals(TestData.MessageWithOptionals.Position.FIRST, parsed.c, "Expected default value for field `c`.")
+ assertEquals(99, parsed.d, "Expected default value for field `d`.")
+ assertEquals(emptyList(), parsed.eList, "Expected default value for field `e`.")
+ }
+
+ @Test
+ fun testThatDefaultValuesAreEncodedCorrectly() {
+ val data = MessageWithOptionals(0, "", MessageWithOptionals.Position.FIRST, 99, emptyList())
+ val parsed = TestData.MessageWithOptionals.parseFrom(protoBuf.encodeToByteArray(data))
+
+ assertTrue(parsed.hasA(), "Expected that custom value for optional field `a` is encoded.")
+ assertTrue(parsed.hasB(), "Expected that custom value for optional field `b` is encoded.")
+ assertTrue(parsed.hasC(), "Expected that custom value for optional field `c` is encoded.")
+ assertTrue(parsed.hasD(), "Expected that custom value for optional field `d` is encoded.")
+
+ assertEquals(0, parsed.a, "Expected custom value for field `a`.")
+ assertEquals("", parsed.b, "Expected custom value for field `b`.")
+ assertEquals(TestData.MessageWithOptionals.Position.FIRST, parsed.c, "Expected custom value for field `c`.")
+ assertEquals(99, parsed.d, "Expected custom value for field `d`.")
+ assertEquals(emptyList(), parsed.eList, "Expected custom value for field `e`.")
+ }
+
+ @Test
+ fun testThatCustomValuesAreEncodedCorrectly() {
+ val data = MessageWithOptionals(42, "Test", MessageWithOptionals.Position.SECOND, 24, listOf(1, 2, 3))
+ val parsed = TestData.MessageWithOptionals.parseFrom(protoBuf.encodeToByteArray(data))
+
+ assertTrue(parsed.hasA(), "Expected that custom value for optional field `a` is encoded.")
+ assertTrue(parsed.hasB(), "Expected that custom value for optional field `b` is encoded.")
+ assertTrue(parsed.hasC(), "Expected that custom value for optional field `c` is encoded.")
+ assertTrue(parsed.hasD(), "Expected that custom value for optional field `d` is encoded.")
+
+ assertEquals(42, parsed.a, "Expected custom value for field `a`.")
+ assertEquals("Test", parsed.b, "Expected custom value for field `b`.")
+ assertEquals(TestData.MessageWithOptionals.Position.SECOND, parsed.c, "Expected custom value for field `c`.")
+ assertEquals(24, parsed.d, "Expected custom value for field `d`.")
+ assertEquals(listOf(1, 2, 3), parsed.eList, "Expected custom value for field `e`.")
+ }
+
+ @Test
+ fun testThatNullValuesAreNotDecoded() {
+ val data = TestData.MessageWithOptionals.newBuilder().build()
+ val parsed = protoBuf.decodeFromByteArray<MessageWithOptionals>(data.toByteArray())
+
+ assertFalse(parsed.hasA(), "Expected that null value for optional field `a` is not decoded.")
+ assertFalse(parsed.hasB(), "Expected that null value for optional field `b` is not decoded.")
+ assertFalse(parsed.hasC(), "Expected that null value for optional field `c` is not decoded.")
+ assertFalse(parsed.hasD(), "Expected that null value for optional field `d` is not decoded.")
+
+ assertEquals(0, parsed.a, "Expected default value for field `a`.")
+ assertEquals("", parsed.b, "Expected default value for field `b`.")
+ assertEquals(MessageWithOptionals.Position.FIRST, parsed.c, "Expected default value for field `c`.")
+ assertEquals(99, parsed.d, "Expected default value for field `d`.")
+ assertEquals(emptyList(), parsed.e, "Expected default value for field `e`.")
+ }
+
+ @Test
+ fun testThatDefaultValuesAreDecodedCorrectly() {
+ val data = TestData.MessageWithOptionals.newBuilder()
+ .setA(0)
+ .setB("")
+ .setC(TestData.MessageWithOptionals.Position.FIRST)
+ .setD(99)
+ .addAllE(emptyList())
+ .build()
+ val parsed = protoBuf.decodeFromByteArray<MessageWithOptionals>(data.toByteArray())
+
+ assertTrue(parsed.hasA(), "Expected that custom value for optional field `a` is decoded.")
+ assertTrue(parsed.hasB(), "Expected that custom value for optional field `b` is decoded.")
+ assertTrue(parsed.hasC(), "Expected that custom value for optional field `c` is decoded.")
+ assertTrue(parsed.hasD(), "Expected that custom value for optional field `d` is decoded.")
+
+ assertEquals(0, parsed.a, "Expected custom value for field `a`.")
+ assertEquals("", parsed.b, "Expected custom value for field `b`.")
+ assertEquals(MessageWithOptionals.Position.FIRST, parsed.c, "Expected custom value for field `c`.")
+ assertEquals(99, parsed.d, "Expected custom value for field `d`.")
+ assertEquals(emptyList(), parsed.e, "Expected custom value for field `e`.")
+ }
+
+ @Test
+ fun testThatCustomValuesAreDecodedCorrectly() {
+ val data = TestData.MessageWithOptionals.newBuilder()
+ .setA(42)
+ .setB("Test")
+ .setC(TestData.MessageWithOptionals.Position.SECOND)
+ .setD(24)
+ .addAllE(listOf(1, 2, 3))
+ .build()
+ val parsed = protoBuf.decodeFromByteArray<MessageWithOptionals>(data.toByteArray())
+
+ assertTrue(parsed.hasA(), "Expected that custom value for optional field `a` is decoded.")
+ assertTrue(parsed.hasB(), "Expected that custom value for optional field `b` is decoded.")
+ assertTrue(parsed.hasC(), "Expected that custom value for optional field `c` is decoded.")
+ assertTrue(parsed.hasD(), "Expected that custom value for optional field `d` is decoded.")
+
+ assertEquals(42, parsed.a, "Expected custom value for field `a`.")
+ assertEquals("Test", parsed.b, "Expected custom value for field `b`.")
+ assertEquals(MessageWithOptionals.Position.SECOND, parsed.c, "Expected custom value for field `c`.")
+ assertEquals(24, parsed.d, "Expected custom value for field `d`.")
+ assertEquals(listOf(1, 2, 3), parsed.e, "Expected custom value for field `e`.")
+ }
+
+ /**
+ * Test [Serializable] that manually implements `TestOptional` defined in `test_data.proto`.
+ *
+ * Using `null` as default values allows to implement [hasA], ... according to Java ProtoBuf library.
+ */
+ @Serializable
+ private data class MessageWithOptionals(
+ private val _a: Int? = null,
+ private val _b: String? = null,
+ private val _c: Position? = null,
+ private val _d: Int? = null,
+ private val _e: List<Int>? = null
+ ) : IMessage {
+
+ val a: Int
+ get() = _a ?: 0
+
+ val b: String
+ get() = _b ?: ""
+
+ val c: Position
+ get() = _c ?: Position.FIRST
+
+ val d: Int
+ get() = _d ?: 99
+
+ val e: List<Int>
+ get() = _e ?: emptyList()
+
+ fun hasA() = _a != null
+
+ fun hasB() = _b != null
+
+ fun hasC() = _c != null
+
+ fun hasD() = _d != null
+
+ /**
+ * Convert this [Serializable] object to its expected [TestData.MessageWithOptionals] ProtoBuf message.
+ *
+ * For this test we expect that `null` values are not encoded.
+ */
+ override fun toProtobufMessage(): TestData.MessageWithOptionals =
+ TestData.MessageWithOptionals.newBuilder().also { builder ->
+ if (_a != null) builder.a = _a
+ if (_b != null) builder.b = _b
+ if (_c != null) builder.c = _c.toProtoBuf()
+ if (_d != null) builder.d = _d
+ if (_e != null) builder.addAllE(_e)
+ }.build()
+
+ enum class Position {
+ FIRST, SECOND;
+
+ fun toProtoBuf() = when (this) {
+ FIRST -> TestData.MessageWithOptionals.Position.FIRST
+ SECOND -> TestData.MessageWithOptionals.Position.SECOND
+ }
+ }
+ }
+}
diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoBufOptionalTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoBufOptionalTest.kt
new file mode 100644
index 00000000..1c0a82a4
--- /dev/null
+++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoBufOptionalTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import org.junit.Test
+import kotlin.test.*
+
+/**
+ * Test that [ProtoBuf] works correctly if [ProtoBuf.encodeDefaults] is set to `false`.
+ *
+ * In this case default values should not get encoded into bytes.
+ */
+class ProtoBufOptionalTest {
+
+ /** ProtoBuf instance that does **not** encode defaults. */
+ private val protoBuf = ProtoBuf { encodeDefaults = false }
+
+ @Test
+ fun readCompareWithDefaults() {
+ val data = MessageWithOptionals()
+ assertTrue(readCompare(data, alwaysPrint = true, protoBuf = protoBuf))
+ }
+
+ @Test
+ fun dumpCompareWithDefaults() {
+ val data = MessageWithOptionals()
+ assertTrue(dumpCompare(data, alwaysPrint = true, protoBuf = protoBuf))
+ }
+
+ @Test
+ fun readCompareWithValues() {
+ val data = MessageWithOptionals(42, "Test", MessageWithOptionals.Position.SECOND, 24, listOf(1, 2, 3))
+ assertTrue(readCompare(data, alwaysPrint = true, protoBuf = protoBuf))
+ }
+
+ @Test
+ fun dumpCompareWithValues() {
+ val data = MessageWithOptionals(42, "Test", MessageWithOptionals.Position.SECOND, 24, listOf(1, 2, 3))
+ assertTrue(dumpCompare(data, alwaysPrint = true, protoBuf = protoBuf))
+ }
+
+ @Test
+ fun testThatDefaultValuesAreNotEncoded() {
+ val data = MessageWithOptionals()
+ val parsed = TestData.MessageWithOptionals.parseFrom(protoBuf.encodeToByteArray(data))
+
+ assertFalse(parsed.hasA(), "Expected that default value for optional field `a` is not encoded.")
+ assertFalse(parsed.hasB(), "Expected that default value for optional field `b` is not encoded.")
+ assertFalse(parsed.hasC(), "Expected that default value for optional field `c` is not encoded.")
+ assertFalse(parsed.hasD(), "Expected that default value for optional field `d` is not encoded.")
+
+ assertEquals(0, parsed.a, "Expected default value for field `a`.")
+ assertEquals("", parsed.b, "Expected default value for field `b`.")
+ assertEquals(TestData.MessageWithOptionals.Position.FIRST, parsed.c, "Expected default value for field `c`.")
+ assertEquals(99, parsed.d, "Expected default value for field `d`.")
+ assertEquals(emptyList(), parsed.eList, "Expected default value for field `e`.")
+ }
+
+ @Test
+ fun testThatCustomValuesAreEncodedCorrectly() {
+ val data = MessageWithOptionals(42, "Test", MessageWithOptionals.Position.SECOND, 24, listOf(1, 2, 3))
+ val parsed = TestData.MessageWithOptionals.parseFrom(protoBuf.encodeToByteArray(data))
+
+ assertTrue(parsed.hasA(), "Expected that custom value for optional field `a` is encoded.")
+ assertTrue(parsed.hasB(), "Expected that custom value for optional field `b` is encoded.")
+ assertTrue(parsed.hasC(), "Expected that custom value for optional field `c` is encoded.")
+ assertTrue(parsed.hasD(), "Expected that custom value for optional field `d` is encoded.")
+
+ assertEquals(42, parsed.a, "Expected custom value for field `a`.")
+ assertEquals("Test", parsed.b, "Expected custom value for field `b`.")
+ assertEquals(TestData.MessageWithOptionals.Position.SECOND, parsed.c, "Expected custom value for field `c`.")
+ assertEquals(24, parsed.d, "Expected custom value for field `d`.")
+ assertEquals(listOf(1, 2, 3), parsed.eList, "Expected custom value for field `e`.")
+ }
+
+ /**
+ * Test [Serializable] that manually implements `TestOptional` defined in `test_data.proto`.
+ */
+ @Serializable
+ data class MessageWithOptionals(
+ val a: Int = 0,
+ val b: String = "",
+ val c: Position = Position.FIRST,
+ val d: Int = 99,
+ val e: List<Int> = emptyList()
+ ) : IMessage {
+
+ /**
+ * Convert this [Serializable] object to its expected [TestData.MessageWithOptionals] ProtoBuf message.
+ *
+ * For this test we expect that default values are not encoded.
+ */
+ override fun toProtobufMessage(): TestData.MessageWithOptionals =
+ TestData.MessageWithOptionals.newBuilder().also { builder ->
+ val defaults = MessageWithOptionals()
+ if (a != defaults.a) builder.a = a
+ if (b != defaults.b) builder.b = b
+ if (c != defaults.c) builder.c = c.toProtoBuf()
+ if (d != defaults.d) builder.d = d
+ if (e != defaults.e) builder.addAllE(e)
+ }.build()
+
+ enum class Position {
+ FIRST, SECOND;
+
+ fun toProtoBuf() = when (this) {
+ FIRST -> TestData.MessageWithOptionals.Position.FIRST
+ SECOND -> TestData.MessageWithOptionals.Position.SECOND
+ }
+ }
+ }
+}
diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoCompatibilityTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoCompatibilityTest.kt
new file mode 100644
index 00000000..1dfcd71c
--- /dev/null
+++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtoCompatibilityTest.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import org.junit.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class ProtoCompatibilityTest {
+
+ @Test
+ fun testMap() {
+ val mapData = RandomTest.KTestData.KTestMap(mapOf("a" to "b", "c" to "d"), emptyMap())
+ val kxData = ProtoBuf.encodeToByteArray(mapData)
+ val kxHex = ProtoBuf.encodeToHexString(mapData)
+ val protoHex = mapData.toProtobufMessage().toHex()
+ assertTrue(kxHex.equals(protoHex, ignoreCase = true))
+ val deserializedData: RandomTest.KTestData.KTestMap = ProtoBuf.decodeFromHexString(kxHex)
+ val parsedMsg = mapData.toProtobufMessage().parserForType.parseFrom(kxData)
+ assertEquals(mapData, deserializedData)
+ assertEquals(parsedMsg, deserializedData.toProtobufMessage())
+ }
+}
diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtobufEnumWithSerialIdTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtobufEnumWithSerialIdTest.kt
new file mode 100644
index 00000000..758a6351
--- /dev/null
+++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtobufEnumWithSerialIdTest.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.Serializable
+import org.junit.*
+import kotlin.test.assertTrue
+
+class ProtobufEnumWithSerialIdTest {
+
+ @Serializable
+ enum class EnumWithIds(val id: Int) {
+ @ProtoNumber(10)
+ FIRST(10),
+ @ProtoNumber(20)
+ SECOND(20);
+ }
+
+ @Serializable
+ data class EnumHolder(@ProtoNumber(5) val a: EnumWithIds) : IMessage {
+ override fun toProtobufMessage(): TestData.EnumHolder =
+ TestData.EnumHolder.newBuilder().setA(TestData.TestEnumWithIds.forNumber(a.id)).build()
+ }
+
+ @Test
+ fun testEnumsSupportSerialIds() {
+ assertTrue(readCompare(EnumHolder(EnumWithIds.SECOND)))
+ assertTrue(dumpCompare(EnumHolder(EnumWithIds.SECOND)))
+ }
+}
diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtobufTopLevelPrimitivesCompatibilityTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtobufTopLevelPrimitivesCompatibilityTest.kt
new file mode 100644
index 00000000..ccae4a40
--- /dev/null
+++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/ProtobufTopLevelPrimitivesCompatibilityTest.kt
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import com.google.protobuf.*
+import kotlinx.serialization.*
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.builtins.*
+import org.junit.Test
+import java.io.*
+import kotlin.test.*
+
+class ProtobufTopLevelPrimitivesCompatibilityTest {
+
+ @Serializable
+ data class Box(@ProtoNumber(1) val i: Int)
+
+ @Serializable
+ data class StringHolder(val foo: String)
+
+ @Test
+ fun testPrimitivesCompatibility() {
+ testCompatibility(true, Boolean.serializer(), "01") { writeBoolNoTag(it) }
+ testCompatibility('c', Char.serializer(), "63") { writeInt32NoTag(it.code) }
+ testCompatibility(1, Byte.serializer(), "01") { writeInt32NoTag(it.toInt()) }
+ testCompatibility(1, Short.serializer(), "01") { writeInt32NoTag(it.toInt()) }
+ testCompatibility(1, Int.serializer(), "01") { writeInt32NoTag(it) }
+ testCompatibility(1, Long.serializer(), "01") { writeInt64NoTag(it) }
+ testCompatibility(1f, Float.serializer(), "0000803F") { writeFloatNoTag(it) }
+ testCompatibility(1.0, Double.serializer(), "000000000000F03F") { writeDoubleNoTag(it) }
+ testCompatibility("string", String.serializer(), "06737472696E67") { writeStringNoTag(it) }
+ }
+
+ @Test
+ fun testArraysCompatibility() {
+ testCompatibility(byteArrayOf(1, 2, 3), ByteArraySerializer(), "03010203") { writeByteArrayNoTag(it) }
+ testCompatibility(byteArrayOf(), ByteArraySerializer(), "00") { writeByteArrayNoTag(it) }
+ testCompatibility(intArrayOf(1, 2, 3), IntArraySerializer(), "03010203") {
+ writeUInt32NoTag(it.size)
+ for (i in it) writeInt32NoTag(i)
+ }
+
+ testCompatibility(arrayOf(Box(1), Box(2)), serializer(), "02020801020802") {
+ writeUInt32NoTag(it.size)
+ for (box in it) {
+ writeInt32NoTag(2) // Size in bytes
+ writeInt32(1, box.i)
+ }
+ }
+
+ testCompatibility(arrayOf<Box>(), serializer(), "00") {
+ writeUInt32NoTag(it.size)
+ }
+ }
+
+ @Test
+ fun testListsCompatibility() {
+ testCompatibility(listOf(1, 2, 3), serializer(), "03010203") {
+ writeUInt32NoTag(it.size)
+ for (i in it) writeInt32NoTag(i)
+ }
+ testCompatibility(listOf(Box(1), Box(2)), serializer(), "02020801020802") {
+ writeUInt32NoTag(it.size)
+ for (box in it) {
+ writeInt32NoTag(2) // Size-prefix
+ writeInt32(1, box.i)
+ }
+ }
+
+ testCompatibility(listOf(StringHolder("a"), StringHolder("bb")), serializer(), "02030A0161040A026262") {
+ writeUInt32NoTag(it.size)
+ for (holder in it) {
+ writeInt32NoTag(holder.foo.protoSize)
+ writeString(1, holder.foo)
+ }
+ }
+
+ testCompatibility(listOf<Int>(), serializer(), "00") {
+ writeUInt32NoTag(it.size)
+ }
+ }
+
+
+ @Test
+ fun testNestedListsCompatibility() {
+ testCompatibility(
+ listOf(listOf(StringHolder("a")), listOf(StringHolder("bb"), StringHolder("cc"))),
+ serializer(),
+ "0201030A016102040A026262040A026363"
+ ) {
+ writeUInt32NoTag(it.size) // Top level list size
+ for (list in it) {
+ writeUInt32NoTag(list.size) //Nested list size
+ for (h in list) {
+ writeInt32NoTag(h.foo.protoSize)
+ writeString(1, h.foo)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun testMapsCompatibility() {
+ testCompatibility(mapOf(1 to 2, 3 to 4), serializer(), "0204080110020408031004") {
+ writeUInt32NoTag(it.size)
+ for (pair in it) {
+ writeInt32NoTag(4) // Size
+ writeInt32(1, pair.key)
+ writeInt32(2, pair.value)
+ }
+ }
+
+ testCompatibility(mapOf<Int, Int>(), serializer(), "00") {
+ writeInt32NoTag(0)
+ }
+ }
+
+ @Test
+ fun testMapOfListsCompatibility() {
+ testCompatibility(
+ mapOf(listOf(1, 2, 3) to listOf("aa"), listOf(7) to listOf("a", "b", "c")),
+ serializer(),
+ "020A080108020803120261610B0807120161120162120163"
+ ) {
+ writeUInt32NoTag(it.size) // outer map size
+ for ((index, entry) in it.entries.withIndex()) {
+ val key = entry.key
+ val value = entry.value
+ // Hand-rolled size of entry
+ val sz = if (index == 0) 10 else 11
+ writeInt32NoTag(sz) // Size of entry
+ for (i in key) writeInt32(1, i)
+ for (s in value) {
+ writeString(2, s)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun testNestedMaps() {
+ // No compatibility on purpose, too much to write
+ val key1 = mapOf(listOf(Box(2), Box(3)) to listOf(StringHolder("aa"), StringHolder("bb")))
+ val value1 = mapOf(listOf("a", "b", "c") to 42)
+ val key2 = mapOf(listOf(Box(42)) to listOf(StringHolder("ff")))
+ val value2 = mapOf(listOf("d", "e") to 42)
+ val map = mapOf(key1 to value1, key2 to value2)
+ assertSerializedToBinaryAndRestored(map, serializer(), ProtoBuf)
+ }
+
+ @Serializable
+ enum class Enum {
+ E1, E2
+ }
+
+ @Test
+ fun testTopLevelEnum() {
+ testCompatibility(Enum.E1, serializer<Enum>(), "00") {
+ writeUInt32NoTag(0)
+ }
+
+ testCompatibility(Enum.E2, serializer<Enum>(), "01") {
+ writeUInt32NoTag(1)
+ }
+ }
+
+ @Serializable
+ object SomeObject
+
+ @Test
+ fun testTopLevelObject() {
+ testCompatibility(SomeObject, serializer(), "") {}
+ }
+
+ private fun <T> testCompatibility(
+ data: T,
+ serializer: KSerializer<T>,
+ expectedHexString: String,
+ block: CodedOutputStream.(T) -> Unit
+ ) {
+ val bytes = ProtoBuf.encodeToByteArray(serializer, data)
+ val string = HexConverter.printHexBinary(bytes)
+ val baos = ByteArrayOutputStream()
+ val cos = CodedOutputStream.newInstance(baos)
+ cos.block(data)
+ cos.flush()
+ assertTrue(
+ baos.toByteArray().contentEquals(bytes),
+ "Original: ${baos.toByteArray().contentToString()},\n\t kx: ${bytes.contentToString()}"
+ )
+ assertEquals(expectedHexString, string)
+ val deserialized = ProtoBuf.decodeFromHexString(serializer, string)
+ when (data) {
+ is Array<*> -> {
+ assertTrue(data.contentEquals(deserialized as Array<out Any?>))
+ }
+ is IntArray -> {
+ assertTrue(data.contentEquals(deserialized as IntArray))
+ }
+ is ByteArray -> {
+ assertTrue(data.contentEquals(deserialized as ByteArray))
+ }
+ else -> {
+ assertEquals(data, deserialized)
+ }
+ }
+ }
+
+ private val String.protoSize: Int get() = 2 + length
+}
diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/RandomTests.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/RandomTests.kt
new file mode 100644
index 00000000..0c94c6e3
--- /dev/null
+++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/RandomTests.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import com.google.protobuf.GeneratedMessageV3
+import io.kotlintest.properties.Gen
+import io.kotlintest.properties.forAll
+import io.kotlintest.specs.ShouldSpec
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.protobuf.TestData.*
+
+
+class RandomTest : ShouldSpec() {
+
+ companion object {
+ fun Gen<String>.generateNotEmpty() = nextPrintableString(Gen.choose(1, 100).generate())
+ }
+
+ object KTestData {
+ @Serializable
+ data class KTestInt32(@ProtoNumber(1) val a: Int) : IMessage {
+ override fun toProtobufMessage(): GeneratedMessageV3 = TestInt32.newBuilder().setA(a).build()
+
+ companion object : Gen<KTestInt32> {
+ override fun generate(): KTestInt32 = KTestInt32(Gen.int().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestSignedInt(@ProtoNumber(1) @ProtoType(ProtoIntegerType.SIGNED) val a: Int) : IMessage {
+ override fun toProtobufMessage(): GeneratedMessageV3 = TestSignedInt.newBuilder().setA(a).build()
+
+ companion object : Gen<KTestSignedInt> {
+ override fun generate(): KTestSignedInt = KTestSignedInt(Gen.int().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestSignedLong(@ProtoNumber(1) @ProtoType(ProtoIntegerType.SIGNED) val a: Long) : IMessage {
+ override fun toProtobufMessage(): GeneratedMessageV3 = TestSignedLong.newBuilder().setA(a).build()
+
+ companion object : Gen<KTestSignedLong> {
+ override fun generate(): KTestSignedLong = KTestSignedLong(Gen.long().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestFixedInt(@ProtoNumber(1) @ProtoType(ProtoIntegerType.FIXED) val a: Int) : IMessage {
+ override fun toProtobufMessage(): GeneratedMessageV3 = TestFixedInt.newBuilder().setA(a).build()
+
+ companion object : Gen<KTestFixedInt> {
+ override fun generate(): KTestFixedInt = KTestFixedInt(Gen.int().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestDouble(@ProtoNumber(1) val a: Double) : IMessage {
+ override fun toProtobufMessage(): GeneratedMessageV3 = TestDouble.newBuilder().setA(a).build()
+
+ companion object : Gen<KTestDouble> {
+ override fun generate(): KTestDouble = KTestDouble(Gen.double().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestBoolean(@ProtoNumber(1) val a: Boolean) : IMessage {
+ override fun toProtobufMessage(): GeneratedMessageV3 = TestBoolean.newBuilder().setA(a).build()
+
+ companion object : Gen<KTestBoolean> {
+ override fun generate(): KTestBoolean = KTestBoolean(Gen.bool().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestAllTypes(
+ @ProtoNumber(1) val i32: Int,
+ @ProtoNumber(2) @ProtoType(ProtoIntegerType.SIGNED) val si32: Int,
+ @ProtoNumber(3) @ProtoType(ProtoIntegerType.FIXED) val f32: Int,
+ @ProtoNumber(10) val i64: Long,
+ @ProtoNumber(11) @ProtoType(ProtoIntegerType.SIGNED) val si64: Long,
+ @ProtoNumber(12) @ProtoType(ProtoIntegerType.FIXED) val f64: Long,
+ @ProtoNumber(21) val f: Float,
+ @ProtoNumber(22) val d: Double,
+ @ProtoNumber(41) val b: Boolean = false,
+ @ProtoNumber(51) val s: String
+ ) : IMessage {
+ override fun toProtobufMessage(): TestAllTypes = TestAllTypes.newBuilder()
+ .setI32(i32)
+ .setSi32(si32)
+ .setF32(f32)
+ .setI64(i64)
+ .setSi64(si64)
+ .setF64(f64)
+ .setF(f)
+ .setD(d)
+ .setB(b)
+ .setS(s)
+ .build()
+
+ companion object : Gen<KTestAllTypes> {
+ override fun generate(): KTestAllTypes = KTestAllTypes(
+ Gen.int().generate(),
+ Gen.int().generate(),
+ Gen.int().generate(),
+ Gen.long().generate(),
+ Gen.long().generate(),
+ Gen.long().generate(),
+ Gen.float().generate(),
+ Gen.double().generate(),
+ Gen.bool().generate(),
+ Gen.string().generateNotEmpty()
+ )
+ }
+ }
+
+ @Serializable
+ data class KTestOuterMessage(
+ @ProtoNumber(1) val a: Int,
+ @ProtoNumber(2) val b: Double,
+ @ProtoNumber(10) val inner: KTestAllTypes,
+ @ProtoNumber(20) val s: String
+ ) : IMessage {
+ override fun toProtobufMessage(): GeneratedMessageV3 = TestOuterMessage.newBuilder()
+ .setA(a)
+ .setB(b)
+ .setInner(inner.toProtobufMessage())
+ .setS(s)
+ .build()
+
+ companion object : Gen<KTestOuterMessage> {
+ override fun generate(): KTestOuterMessage = KTestOuterMessage(
+ Gen.int().generate(),
+ Gen.double().generate(),
+ KTestAllTypes.generate(),
+ Gen.string().generateNotEmpty()
+ )
+ }
+ }
+
+ @Serializable
+ data class KTestIntListMessage(
+ @ProtoNumber(1) val s: Int,
+ @ProtoNumber(10) val l: List<Int>
+ ) : IMessage {
+ override fun toProtobufMessage(): GeneratedMessageV3 = TestRepeatedIntMessage.newBuilder().setS(s).addAllB(l).build()
+
+ companion object : Gen<KTestIntListMessage> {
+ override fun generate() = KTestIntListMessage(Gen.int().generate(), Gen.list(Gen.int()).generate())
+ }
+ }
+
+ @Serializable
+ data class KTestObjectListMessage(
+ @ProtoNumber(1) val inner: List<KTestAllTypes>
+ ) : IMessage {
+ override fun toProtobufMessage(): GeneratedMessageV3 = TestRepeatedObjectMessage.newBuilder().addAllInner(inner.map { it.toProtobufMessage() }).build()
+
+ companion object : Gen<KTestObjectListMessage> {
+ override fun generate() = KTestObjectListMessage(Gen.list(KTestAllTypes.Companion).generate())
+ }
+ }
+
+ enum class KCoffee { AMERICANO, LATTE, CAPPUCCINO }
+
+ @Serializable
+ data class KTestEnum(@ProtoNumber(1) val a: KCoffee): IMessage {
+ override fun toProtobufMessage() = TestEnum.newBuilder().setA(TestEnum.Coffee.forNumber(a.ordinal)).build()
+
+ companion object : Gen<KTestEnum> {
+ override fun generate(): KTestEnum = KTestEnum(Gen.oneOf<KCoffee>().generate())
+ }
+ }
+
+ @Serializable
+ data class KTestMap(@ProtoNumber(1) val s: Map<String, String>, @ProtoNumber(2) val o: Map<Int, KTestAllTypes> = emptyMap()) :
+ IMessage {
+ override fun toProtobufMessage() = TestMap.newBuilder()
+ .putAllStringMap(s)
+ .putAllIntObjectMap(o.mapValues { it.value.toProtobufMessage() })
+ .build()
+
+ companion object : Gen<KTestMap> {
+ override fun generate(): KTestMap =
+ KTestMap(Gen.map(Gen.string(), Gen.string()).generate(), Gen.map(Gen.int(), KTestAllTypes).generate())
+ }
+ }
+ }
+
+ init {
+ "Protobuf serialization" {
+ should("serialize random int32") { forAll(KTestData.KTestInt32.Companion) { dumpCompare(it) } }
+ should("serialize random signed int32") { forAll(KTestData.KTestSignedInt.Companion) { dumpCompare(it) } }
+ should("serialize random signed int64") { forAll(KTestData.KTestSignedLong.Companion) { dumpCompare(it) } }
+ should("serialize random fixed int32") { forAll(KTestData.KTestFixedInt.Companion) { dumpCompare(it) } }
+ should("serialize random doubles") { forAll(KTestData.KTestDouble.Companion) { dumpCompare(it) } }
+ should("serialize random booleans") { forAll(KTestData.KTestBoolean.Companion) { dumpCompare(it) } }
+ should("serialize random enums") { forAll(KTestData.KTestEnum.Companion) { dumpCompare(it) } }
+ should("serialize all base random types") { forAll(KTestData.KTestAllTypes.Companion) { dumpCompare(it) } }
+ should("serialize random messages with embedded message") { forAll(KTestData.KTestOuterMessage.Companion) { dumpCompare(it) } }
+ should("serialize random messages with primitive list fields as repeated") { forAll(KTestData.KTestIntListMessage.Companion) { dumpCompare(it) } }
+ should("serialize messages with object list fields as repeated") { forAll(KTestData.KTestObjectListMessage.Companion) { dumpCompare(it) } }
+ should("serialize messages with scalar-key maps") { forAll(KTestData.KTestMap.Companion) { dumpCompare(it) } }
+ }
+
+ "Protobuf deserialization" {
+ should("read random int32") { forAll(KTestData.KTestInt32.Companion) { readCompare(it) } }
+ should("read random signed int32") { forAll(KTestData.KTestSignedInt.Companion) { readCompare(it) } }
+ should("read random signed int64") { forAll(KTestData.KTestSignedLong.Companion) { readCompare(it) } }
+ should("read random fixed int32") { forAll(KTestData.KTestFixedInt.Companion) { readCompare(it) } }
+ should("read random doubles") { forAll(KTestData.KTestDouble.Companion) { readCompare(it) } }
+ should("read random enums") { forAll(KTestData.KTestEnum.Companion) { readCompare(it) } }
+ should("read all base random types") { forAll(KTestData.KTestAllTypes.Companion) { readCompare(it) } }
+ should("read random messages with embedded message") { forAll(KTestData.KTestOuterMessage.Companion) { readCompare(it) } }
+ should("read random messages with primitive list fields as repeated") { forAll(KTestData.KTestIntListMessage.Companion) { readCompare(it) } }
+ should("read random messages with object list fields as repeated") { forAll(KTestData.KTestObjectListMessage.Companion) { readCompare(it) } }
+ should("read messages with scalar-key maps") { forAll(KTestData.KTestMap.Companion) { readCompare(it) } }
+ }
+ }
+}
diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/schema/GenerationTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/schema/GenerationTest.kt
new file mode 100644
index 00000000..7b075a4e
--- /dev/null
+++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/schema/GenerationTest.kt
@@ -0,0 +1,213 @@
+package kotlinx.serialization.protobuf.schema
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.protobuf.ProtoIntegerType
+import kotlinx.serialization.protobuf.*
+import kotlin.reflect.KClass
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+internal const val TARGET_PACKAGE = "kotlinx.serialization.protobuf.schema.generator"
+internal const val COMMON_SCHEMA_FILE_NAME = "common/schema.proto"
+internal val commonClasses = listOf(
+ GenerationTest.ScalarHolder::class,
+ GenerationTest.FieldNumberClass::class,
+ GenerationTest.SerialNameClass::class,
+ GenerationTest.ListClass::class,
+ GenerationTest.PackedListClass::class,
+ GenerationTest.MapClass::class,
+ GenerationTest.OptionalClass::class,
+ GenerationTest.ContextualHolder::class,
+ GenerationTest.AbstractHolder::class,
+ GenerationTest.SealedHolder::class,
+ GenerationTest.NestedCollections::class,
+ GenerationTest.LegacyMapHolder::class,
+ GenerationTest.NullableNestedCollections::class,
+ GenerationTest.OptionalCollections::class,
+)
+
+class GenerationTest {
+ @Serializable
+ class ScalarHolder(
+ val int: Int,
+ @ProtoType(ProtoIntegerType.SIGNED)
+ val intSigned: Int,
+ @ProtoType(ProtoIntegerType.FIXED)
+ val intFixed: Int,
+ @ProtoType(ProtoIntegerType.DEFAULT)
+ val intDefault: Int,
+
+ val long: Long,
+ @ProtoType(ProtoIntegerType.SIGNED)
+ val longSigned: Long,
+ @ProtoType(ProtoIntegerType.FIXED)
+ val longFixed: Long,
+ @ProtoType(ProtoIntegerType.DEFAULT)
+ val longDefault: Int,
+
+ val flag: Boolean,
+ val byteArray: ByteArray,
+ val boxedByteArray: Array<Byte?>,
+ val text: String,
+ val float: Float,
+ val double: Double
+ )
+
+ @Serializable
+ class FieldNumberClass(
+ val a: Int,
+ @ProtoNumber(5)
+ val b: Int,
+ @ProtoNumber(3)
+ val c: Int
+ )
+
+ @Serializable
+ @SerialName("OverriddenClassName")
+ class SerialNameClass(
+ val original: Int,
+ @SerialName("OverriddenFieldName")
+ val b: SerialNameEnum
+ )
+
+ @Serializable
+ @SerialName("OverriddenEnumName")
+ enum class SerialNameEnum {
+ FIRST,
+
+ @SerialName("OverriddenElementName")
+ SECOND
+ }
+
+ @Serializable
+ data class OptionsClass(val i: Int)
+
+ @Serializable
+ class ListClass(
+ val intList: List<Int>,
+ val intArray: IntArray,
+ val boxedIntArray: Array<Int?>,
+ val messageList: List<OptionsClass>,
+ val enumList: List<SerialNameEnum>
+ )
+
+ @Serializable
+ class PackedListClass(
+ @ProtoPacked val intList: List<Int>,
+ @ProtoPacked val intArray: IntArray,
+ @ProtoPacked val boxedIntArray: Array<Int?>,
+ val messageList: List<OptionsClass>,
+ val enumList: List<SerialNameEnum>
+ )
+
+ @Serializable
+ class MapClass(
+ val scalarMap: Map<Int, Float>,
+ val bytesMap: Map<Int, List<Byte>>,
+ val messageMap: Map<String, OptionsClass>,
+ val enumMap: Map<Boolean, SerialNameEnum>
+ )
+
+ @Serializable
+ data class OptionalClass(
+ val requiredInt: Int,
+ val optionalInt: Int = 5,
+ val nullableInt: Int?,
+ val nullableOptionalInt: Int? = 10
+ )
+
+ @Serializable
+ data class OptionalCollections(
+ val requiredList: List<Int>,
+ val optionalList: List<Int> = listOf(42),
+ val nullableList: List<Int>?,
+ val nullableOptionalList: List<Int>? = listOf(42),
+
+ val requiredMap: Map<Int, Int>,
+ val optionalMap: Map<Int, Int> = mapOf(42 to 42),
+ val nullableMap: Map<Int, Int>?,
+ val nullableOptionalMap: Map<Int, Int>? = mapOf(42 to 42)
+ )
+
+ @Serializable
+ data class ContextualHolder(
+ @Contextual val value: Int
+ )
+
+ @Serializable
+ abstract class AbstractClass(val int: Int)
+
+ @Serializable
+ data class AbstractHolder(@Polymorphic val abs: AbstractClass)
+
+ @Serializable
+ sealed class SealedClass {
+ @Serializable
+ data class Impl1(val int: Int) : SealedClass()
+
+ @Serializable
+ data class Impl2(val long: Long) : SealedClass()
+ }
+
+ @Serializable
+ data class SealedHolder(val sealed: SealedClass)
+
+ @Serializable
+ class NestedCollections(
+ val intList: List<List<Int>>,
+ val messageList: List<List<OptionsClass>>,
+ val mapInList: List<Map<String, OptionsClass>>,
+ val listInMap: Map<String, List<Int>>
+ )
+
+ @Serializable
+ class LegacyMapHolder(
+ val keyAsMessage: Map<OptionsClass, Int>,
+ val keyAsEnum: Map<SerialNameEnum, OptionsClass>,
+ val keyAsBytes: Map<List<Byte>, List<Byte>>,
+ val keyAsList: Map<List<Int>, List<Byte>>,
+ val keyAsDeepList: Map<List<List<Int>>, List<Byte>>,
+ val nullableKeyAndValue: Map<OptionsClass?, OptionsClass?>
+ )
+
+ @Serializable
+ class NullableNestedCollections(
+ val nullableIntList: List<List<Int>?>,
+ val nullableIntMap: Map<String, List<Int>?>,
+ val intMap: Map<String, List<Int?>>,
+ val intList: List<List<Int?>>,
+ val legacyMap: Map<List<Int>?, List<Int>?>
+ )
+
+ @Test
+ fun testIndividuals() {
+ assertSchemaForClass(OptionsClass::class, mapOf("java_package" to "api.proto", "java_outer_classname" to "Outer"))
+ commonClasses.forEach {
+ assertSchemaForClass(it)
+ }
+ }
+
+ @Test
+ fun testCommon() {
+ assertSchema(COMMON_SCHEMA_FILE_NAME, commonClasses.map { it.serializer().descriptor }.toList())
+ }
+
+ private fun assertSchemaForClass(
+ clazz: KClass<*>,
+ options: Map<String, String> = emptyMap()
+ ) {
+ assertSchema("${clazz.simpleName}.proto", listOf(clazz.serializer().descriptor), options)
+ }
+
+ private fun assertSchema(
+ fileName: String,
+ descriptors: List<SerialDescriptor>,
+ options: Map<String, String> = emptyMap()
+ ) {
+ val schema = this::class.java.getResourceAsStream("/$fileName")
+ .readBytes().toString(Charsets.UTF_8)
+ .replace("\r\n", "\n") // fixme when compiled on windows, the file contains line \r\n breaks
+ assertEquals(schema, ProtoBufSchemaGenerator.generateSchemaText(descriptors, TARGET_PACKAGE, options))
+ }
+}
diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/schema/RegenerateSchemas.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/schema/RegenerateSchemas.kt
new file mode 100644
index 00000000..736627ae
--- /dev/null
+++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/schema/RegenerateSchemas.kt
@@ -0,0 +1,45 @@
+package kotlinx.serialization.protobuf.schema
+
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.serializer
+import java.io.File
+import java.nio.charset.StandardCharsets
+import kotlin.reflect.KClass
+
+private const val RESOURCES_ROOT_PATH = "formats/protobuf/jvmTest/resources"
+
+// Regenerate all proto file for tests.
+private fun main() {
+ regenerateAllProtoFiles()
+}
+
+private fun regenerateAllProtoFiles() {
+ generateProtoFile(
+ GenerationTest.OptionsClass::class,
+ mapOf("java_package" to "api.proto", "java_outer_classname" to "Outer")
+ )
+
+ commonClasses.forEach { generateProtoFile(it) }
+ generateCommonProtoFile(commonClasses)
+}
+
+private fun generateProtoFile(clazz: KClass<*>, options: Map<String, String> = emptyMap()
+) {
+ generateSchemaFile("${clazz.simpleName}.proto", listOf(clazz.serializer().descriptor), options)
+}
+
+private fun generateCommonProtoFile(classes: List<KClass<*>>) {
+ val descriptors = classes.map { it.serializer().descriptor }.toList()
+ generateSchemaFile(COMMON_SCHEMA_FILE_NAME, descriptors)
+}
+
+private fun generateSchemaFile(
+ fileName: String,
+ descriptors: List<SerialDescriptor>,
+ options: Map<String, String> = emptyMap()
+) {
+ val filePath = "$RESOURCES_ROOT_PATH/$fileName"
+ val file = File(filePath)
+ val schema = ProtoBufSchemaGenerator.generateSchemaText(descriptors, TARGET_PACKAGE, options)
+ file.writeText(schema, StandardCharsets.UTF_8)
+}
diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt
new file mode 100644
index 00000000..38b7d388
--- /dev/null
+++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt
@@ -0,0 +1,7 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+actual val currentPlatform: Platform = Platform.JVM
diff --git a/formats/protobuf/nativeMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt b/formats/protobuf/nativeMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt
new file mode 100644
index 00000000..72fbfd01
--- /dev/null
+++ b/formats/protobuf/nativeMain/src/kotlinx/serialization/protobuf/internal/Bytes.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf.internal
+
+private fun Short.reverseBytes(): Short = (((this.toInt() and 0xff) shl 8) or ((this.toInt() and 0xffff) ushr 8)).toShort()
+
+internal actual fun Int.reverseBytes(): Int =
+ ((this and 0xffff).toShort().reverseBytes().toInt() shl 16) or ((this ushr 16).toShort().reverseBytes().toInt() and 0xffff)
+
+internal actual fun Long.reverseBytes(): Long =
+ ((this and 0xffffffff).toInt().reverseBytes().toLong() shl 32) or ((this ushr 32).toInt()
+ .reverseBytes().toLong() and 0xffffffff)
diff --git a/formats/protobuf/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/formats/protobuf/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
new file mode 100644
index 00000000..36221960
--- /dev/null
+++ b/formats/protobuf/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.test
+
+
+import kotlin.native.concurrent.SharedImmutable
+
+
+@SharedImmutable
+public actual val currentPlatform: Platform = Platform.NATIVE
diff --git a/formats/protobuf/testProto/test_data.proto b/formats/protobuf/testProto/test_data.proto
new file mode 100644
index 00000000..f4b1f5f0
--- /dev/null
+++ b/formats/protobuf/testProto/test_data.proto
@@ -0,0 +1,91 @@
+syntax = "proto2";
+option java_package = "kotlinx.serialization.protobuf";
+
+message TestInt32 {
+ required int32 a = 1;
+}
+
+message TestSignedInt {
+ required sint32 a = 1;
+}
+
+message TestSignedLong {
+ required sint64 a = 1;
+}
+
+message TestFixedInt {
+ required fixed32 a = 1;
+}
+
+message TestDouble {
+ required double a = 1;
+}
+
+message TestBoolean {
+ required bool a = 1;
+}
+
+message TestAllTypes {
+ required int32 i32 = 1;
+ required sint32 si32 = 2;
+ required fixed32 f32 = 3;
+ required int64 i64 = 10;
+ required sint64 si64 = 11;
+ required fixed64 f64 = 12;
+ required float f = 21;
+ required double d = 22;
+ required bool b = 41;
+ required string s = 51;
+}
+
+message TestOuterMessage {
+ required int32 a = 1;
+ required double b = 2;
+ required TestAllTypes inner = 10;
+ required string s = 20;
+}
+
+message TestRepeatedIntMessage {
+ required int32 s = 1;
+ repeated int32 b = 10;
+}
+
+message TestRepeatedObjectMessage {
+ repeated TestAllTypes inner = 1;
+}
+
+message TestEnum {
+ enum Coffee {
+ Americano = 0;
+ Latte = 1;
+ Capuccino = 2;
+ }
+ required Coffee a = 1;
+}
+
+enum TestEnumWithIds {
+ First = 10;
+ Second = 20;
+}
+
+message EnumHolder {
+ required TestEnumWithIds a = 5;
+}
+
+message TestMap {
+ map<string, string> stringMap = 1;
+ map<int32, TestAllTypes> intObjectMap = 2;
+}
+
+message MessageWithOptionals {
+ optional int32 a = 1;
+ optional string b = 2;
+ optional Position c = 3;
+ optional int32 d = 4 [default = 99];
+ repeated int32 e = 5;
+
+ enum Position {
+ FIRST = 0;
+ SECOND = 1;
+ }
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..0c58f546
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,40 @@
+#
+# Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+#
+
+group=org.jetbrains.kotlinx
+version=1.3.4-SNAPSHOT
+
+kotlin.version=1.6.21
+
+# This version take precedence if 'bootstrap' property passed to project
+kotlin.version.snapshot=1.6.255-SNAPSHOT
+# Also set KONAN_LOCAL_DIST environment variable in bootstrap mode to auto-assign konan.home
+
+junit_version=4.12
+jackson_version=2.10.0.pr1
+dokka_version=1.6.0
+native.deploy=
+validator_version=0.7.1
+knit_version=0.3.0
+coroutines_version=1.3.9
+kover_version=0.4.2
+
+kover.enabled=true
+
+kotlin.mpp.enableGranularSourceSetsMetadata=true
+kotlin.mpp.enableCompatibilityMetadataVariant=true
+kotlin.mpp.stability.nowarn=true
+
+kotlin.js.compiler=both
+kotlin.incremental.multiplatform=true
+
+org.gradle.parallel=true
+org.gradle.caching=true
+
+#kotlin.native.jvmArgs=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5007
+#kotlin.native.disableCompilerDaemon=true
+
+kotlin.native.distribution.type=prebuilt
+
+org.gradle.jvmargs="-XX:+HeapDumpOnOutOfMemoryError"
diff --git a/gradle/benchmark-parsing.gradle b/gradle/benchmark-parsing.gradle
new file mode 100644
index 00000000..b09ef998
--- /dev/null
+++ b/gradle/benchmark-parsing.gradle
@@ -0,0 +1,26 @@
+import groovy.json.JsonSlurper
+import org.gradle.api.*
+
+/**
+ * Utility for printing benchmark results.
+ * Results can be obtained with JMH flags
+ * -rf json -rff serialization-benchmark-results.json
+ */
+class PrintBenchmarksTask extends DefaultTask {
+ private String fileName = "serialization-benchmark-results.json"
+
+ @TaskAction
+ def printBenchmarkJsonAsTeamcityStats() {
+ File jsonFile = project.file(fileName)
+ if (!jsonFile.exists()) throw new TaskExecutionException(this, new FileNotFoundException("File $fileName not found"))
+ def parsedJson = new JsonSlurper().parseText(jsonFile.text)
+
+ parsedJson.each { v ->
+ def name = (v.benchmark - "kotlinx.benchmarks.")
+ def score = v.primaryMetric.score
+ println("##teamcity[buildStatisticValue key='" + name + "' value='" + score + "']")
+ }
+ }
+}
+
+rootProject.tasks.register("printBenchmarksJsonAsTeamcityStats", PrintBenchmarksTask)
diff --git a/gradle/compiler-version.gradle b/gradle/compiler-version.gradle
new file mode 100644
index 00000000..b9545292
--- /dev/null
+++ b/gradle/compiler-version.gradle
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+gradle.taskGraph.whenReady {
+ println("Using Kotlin compiler version: $compilerVersion")
+ if (build_snapshot_train) {
+ configure(subprojects.findAll { it.name == "core" }) {
+ configurations.matching { it.name == "kotlinCompilerClasspath" }.all {
+ println "Manifest of kotlin-compiler-embeddable.jar for serialization"
+ resolvedConfiguration.getFiles().findAll { it.name.contains("kotlin-compiler-embeddable") }.each {
+ def manifest = zipTree(it).matching {
+ include 'META-INF/MANIFEST.MF'
+ }.getFiles().first()
+
+ manifest.readLines().each {
+ println it
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/gradle/configure-source-sets.gradle b/gradle/configure-source-sets.gradle
new file mode 100644
index 00000000..e7888eea
--- /dev/null
+++ b/gradle/configure-source-sets.gradle
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+kotlin {
+ jvm {
+ withJava()
+ configure([compilations.main, compilations.test]) {
+ kotlinOptions {
+ jvmTarget = '1.6'
+ freeCompilerArgs += "-Xsuppress-deprecated-jvm-target-warning"
+ }
+ }
+ }
+
+ js {
+ nodejs {}
+ configure([compilations.main, compilations.test]) {
+ kotlinOptions {
+ sourceMap = true
+ moduleKind = "umd"
+ metaInfo = true
+ }
+ }
+ }
+
+ sourceSets.all {
+ kotlin.srcDirs = ["$it.name/src"]
+ resources.srcDirs = ["$it.name/resources"]
+ languageSettings {
+ progressiveMode = true
+
+ optIn("kotlin.Experimental")
+ optIn("kotlin.ExperimentalMultiplatform")
+ optIn("kotlin.ExperimentalStdlibApi")
+ optIn("kotlinx.serialization.InternalSerializationApi")
+ }
+ }
+
+ sourceSets {
+ commonMain {
+ dependencies {
+ api 'org.jetbrains.kotlin:kotlin-stdlib-common'
+ }
+ }
+
+ commonTest {
+ dependencies {
+ api 'org.jetbrains.kotlin:kotlin-test-common'
+ api 'org.jetbrains.kotlin:kotlin-test-annotations-common'
+ }
+ }
+
+ jvmMain {
+ dependencies {
+ api 'org.jetbrains.kotlin:kotlin-stdlib'
+ }
+ }
+
+ jvmTest {
+ dependencies {
+ api 'org.jetbrains.kotlin:kotlin-test-junit'
+ }
+ }
+
+ jsMain {
+ dependencies {
+ api 'org.jetbrains.kotlin:kotlin-stdlib-js'
+ }
+ }
+
+ jsTest {
+ dependencies {
+ api 'org.jetbrains.kotlin:kotlin-test-js'
+ }
+ }
+
+ nativeMain.dependencies {
+ }
+ }
+
+ sourceSets.findAll({ it.name.contains("Test") }).forEach { srcSet ->
+ srcSet.languageSettings {
+ it.optIn("kotlinx.serialization.InternalSerializationApi")
+ it.optIn("kotlinx.serialization.ExperimentalSerializationApi")
+ }
+ }
+
+ sourceSets.matching({ it.name.contains("Main") }).all { srcSet ->
+ project.ext.set("kotlin.mpp.freeCompilerArgsForSourceSet.${srcSet.name}", ["-Xexplicit-api=strict"])
+ }
+
+ targets.all {
+ compilations.main {
+ kotlinOptions {
+ allWarningsAsErrors = true
+ }
+ }
+ }
+
+ def targetsWithoutTestRunners = ["linuxArm32Hfp", "linuxArm64", "mingwX86"]
+ configure(targets) {
+ // Configure additional binaries to run tests in the background
+ if (["macos", "linux", "mingw"].any { name.startsWith(it) && !targetsWithoutTestRunners.contains(name) }) {
+ binaries {
+ test("background", [nativeDebugBuild]) {
+ freeCompilerArgs += ["-trw"]
+ }
+ }
+ testRuns {
+ background { setExecutionSourceFrom(binaries.backgroundDebugTest) }
+ }
+ }
+ }
+}
diff --git a/gradle/dokka.gradle b/gradle/dokka.gradle
new file mode 100644
index 00000000..157ab82e
--- /dev/null
+++ b/gradle/dokka.gradle
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+apply plugin: 'kotlin'
+apply plugin: 'org.jetbrains.dokka'
+
+def documentedSubprojects = ["kotlinx-serialization-core",
+ "kotlinx-serialization-json",
+ "kotlinx-serialization-cbor",
+ "kotlinx-serialization-properties",
+ "kotlinx-serialization-hocon",
+ "kotlinx-serialization-protobuf"]
+subprojects {
+ if (!(name in documentedSubprojects)) return
+ apply plugin: 'org.jetbrains.dokka'
+ dependencies {
+ dokkaPlugin("org.jetbrains.kotlinx:dokka-pathsaver-plugin:$knit_version")
+ }
+
+ tasks.named('dokkaHtmlPartial') {
+ outputDirectory = file("build/dokka")
+ dokkaSourceSets {
+ configureEach {
+ includes.from(rootProject.file('dokka/moduledoc.md').path)
+
+ perPackageOption {
+ matchingRegex.set("kotlinx\\.serialization(\$|\\.).*")
+ reportUndocumented.set(true)
+ skipDeprecated.set(true)
+ }
+
+ // Internal API
+ perPackageOption {
+ matchingRegex.set("kotlinx\\.serialization.internal(\$|\\.).*")
+ suppress.set(true)
+ }
+
+ // Workaround for typealias
+ perPackageOption {
+ matchingRegex.set("kotlinx\\.serialization.protobuf.internal(\$|\\.).*")
+ suppress.set(true)
+ reportUndocumented.set(false)
+ }
+
+ // Deprecated migrations
+ perPackageOption {
+ matchingRegex.set("kotlinx\\.protobuf(\$|\\.).*")
+ reportUndocumented.set(true)
+ skipDeprecated.set(true)
+ }
+
+ // Deprecated migrations
+ perPackageOption {
+ matchingRegex.set("org\\.jetbrains\\.kotlinx\\.serialization\\.config(\$|\\.).*")
+ reportUndocumented.set(false)
+ skipDeprecated.set(true)
+ }
+ }
+ }
+ }
+}
+
+// Knit relies on Dokka task and it's pretty convenient
+task dokka(dependsOn: dokkaHtmlMultiModule) {}
diff --git a/gradle/kover.gradle b/gradle/kover.gradle
new file mode 100644
index 00000000..214520d0
--- /dev/null
+++ b/gradle/kover.gradle
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+apply plugin: 'kover'
+
+tasks.withType(Test) { task ->
+ kover {
+ enabled = rootProject.ext.koverEnabled
+
+ }
+}
+tasks.koverVerify {
+ // Core is mainly uncovered because a lot of serializers are tested with JSON
+ def minPercentage = (project.name.contains("core") || project.name.contains("properties")) ? 45 : 80
+ rule {
+ name = "Minimal line coverage rate in percents"
+ bound {
+ minValue = minPercentage
+ // valueType is 'COVERED_LINES_PERCENTAGE' by default
+ }
+ }
+}
diff --git a/gradle/maven-metadata.gradle b/gradle/maven-metadata.gradle
new file mode 100644
index 00000000..5e4148cc
--- /dev/null
+++ b/gradle/maven-metadata.gradle
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+project.ext.pomConfig = {
+ licenses {
+ license {
+ name "The Apache Software License, Version 2.0"
+ url "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ distribution "repo"
+ }
+ }
+ developers {
+ developer {
+ id "JetBrains"
+ name "JetBrains Team"
+ organization "JetBrains"
+ organizationUrl "http://www.jetbrains.com"
+ }
+ }
+
+ scm {
+ url "https://github.com/Kotlin/kotlinx.serialization"
+ }
+}
+
+project.ext.configureMavenCentralMetadata = { pom ->
+ def root = asNode()
+ root.appendNode('name', project.name)
+ root.appendNode('description', 'Kotlin multiplatform serialization runtime library')
+ root.appendNode('url', 'https://github.com/Kotlin/kotlinx.serialization')
+ root.children().last() + pomConfig
+}
diff --git a/gradle/native-targets.gradle b/gradle/native-targets.gradle
new file mode 100644
index 00000000..909fcec1
--- /dev/null
+++ b/gradle/native-targets.gradle
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+/**
+ * Specifies what subset of Native targets to build
+ *
+ * ALL — all possible for compilation
+ * HOST — host-specific ones, without cross compilation (e.g. do not build linuxX64 on macOS host)
+ * SINGLE — only for current OS (to import into IDEA / quickly run tests)
+ * DISABLED — disable all Native targets (useful with Kotlin compiler built from sources)
+ *
+ * For HOST mode, all targets are still listed in .module file, so HOST mode is useful for release process.
+ */
+enum NativeState { ALL, HOST, SINGLE, DISABLED }
+
+def getNativeState(String description) {
+ if (description == null) return NativeState.SINGLE
+ switch(description.toLowerCase()) {
+ case 'all':
+ case 'true':
+ return NativeState.ALL
+ case 'host':
+ return NativeState.HOST
+ case 'disabled':
+ return NativeState.DISABLED
+ // 'single', 'false', etc
+ default:
+ return NativeState.SINGLE
+ }
+}
+
+project.ext.ideaActive = System.getProperty('idea.active') == 'true'
+project.ext.nativeState = getNativeState(property('native.deploy'))
+project.ext.singleTargetMode = project.ext.ideaActive || (project.ext.nativeState == NativeState.SINGLE)
+
+project.ext.nativeMainSets = []
+project.ext.nativeTestSets = []
+
+/**
+ * Disables compilation but leaves the target in .module file
+ */
+def disableCompilation(targets) {
+ configure(targets) {
+ compilations.all {
+ cinterops.all { project.tasks[interopProcessingTaskName].enabled = false }
+ compileKotlinTask.enabled = false
+ }
+ binaries.all { linkTask.enabled = false }
+
+ mavenPublication { publicationToDisable ->
+ tasks.withType(AbstractPublishToMaven).all {
+ onlyIf { publication != publicationToDisable }
+ }
+ tasks.withType(GenerateModuleMetadata).all {
+ onlyIf { publication.get() != publicationToDisable }
+ }
+ }
+ }
+}
+
+def getHostName() {
+ def target = System.getProperty("os.name")
+ if (target == 'Linux') return 'linux'
+ if (target.startsWith('Windows')) return 'windows'
+ if (target.startsWith('Mac')) return 'macos'
+ return 'unknown'
+}
+
+kotlin {
+ targets {
+ def manager = project.ext.hostManager
+ def linuxEnabled = manager.isEnabled(presets.linuxX64.konanTarget)
+ def macosEnabled = manager.isEnabled(presets.macosX64.konanTarget)
+ def winEnabled = manager.isEnabled(presets.mingwX64.konanTarget)
+
+ def ideaPreset = presets.linuxX64
+ if (macosEnabled) ideaPreset = presets.macosX64
+ if (winEnabled) ideaPreset = presets.mingwX64
+ project.ext.ideaPreset = ideaPreset
+ }
+
+ targets.metaClass.addTarget = { preset ->
+ def target = delegate.fromPreset(preset, preset.name)
+ project.ext.nativeMainSets.add(target.compilations['main'].kotlinSourceSets.first())
+ project.ext.nativeTestSets.add(target.compilations['test'].kotlinSourceSets.first())
+ }
+
+ targets {
+ if (project.ext.nativeState == NativeState.DISABLED) return
+ if (project.ext.singleTargetMode) {
+ fromPreset(project.ext.ideaPreset, 'native')
+ } else {
+ // Linux
+ addTarget(presets.linuxX64)
+ addTarget(presets.linuxArm32Hfp)
+ addTarget(presets.linuxArm64)
+
+ // Mac & iOS
+ addTarget(presets.macosX64)
+
+ addTarget(presets.iosArm64)
+ addTarget(presets.iosArm32)
+ addTarget(presets.iosX64)
+
+ addTarget(presets.watchosX86)
+ addTarget(presets.watchosX64)
+ addTarget(presets.watchosArm32)
+ addTarget(presets.watchosArm64)
+
+ addTarget(presets.tvosArm64)
+ addTarget(presets.tvosX64)
+
+ // Apple Silicon
+ addTarget(presets.iosSimulatorArm64)
+ addTarget(presets.watchosSimulatorArm64)
+ addTarget(presets.tvosSimulatorArm64)
+ addTarget(presets.macosArm64)
+
+ // Windows
+ addTarget(presets.mingwX64)
+ addTarget(presets.mingwX86)
+ }
+
+ if (project.ext.nativeState == NativeState.HOST) {
+ // linux targets that can cross-compile on all three hosts
+ def linuxCrossCompileTargets = [linuxX64, linuxArm32Hfp, linuxArm64]
+ if (getHostName() != "linux") {
+ disableCompilation(linuxCrossCompileTargets)
+ }
+ }
+ }
+
+
+ sourceSets {
+ nativeMain { dependsOn commonMain }
+ // Empty source set is required in order to have native tests task
+ nativeTest { dependsOn commonTest }
+
+ if (!project.ext.singleTargetMode) {
+ configure(project.ext.nativeMainSets) {
+ dependsOn nativeMain
+ }
+
+ configure(project.ext.nativeTestSets) {
+ dependsOn nativeTest
+ }
+ }
+ }
+}
diff --git a/gradle/publish-mpp-root-module-in-platform.gradle b/gradle/publish-mpp-root-module-in-platform.gradle
new file mode 100644
index 00000000..7362ffb4
--- /dev/null
+++ b/gradle/publish-mpp-root-module-in-platform.gradle
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014-2020 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+
+/**
+ * Publish the platform JAR and POM so that consumers who depend on this module and can't read Gradle module
+ * metadata can still get the platform artifact and transitive dependencies from the POM
+ * (see details in https://youtrack.jetbrains.com/issue/KT-39184#focus=streamItem-27-4115233.0-0)
+ */
+project.ext.publishPlatformArtifactsInRootModule = { MavenPublication platformPublication ->
+ afterEvaluate {
+ XmlProvider platformXml = null
+
+ platformPublication.pom.withXml { platformXml = it }
+
+ publishing.publications.kotlinMultiplatform {
+ pom.withXml {
+ Node root = asNode()
+ // Remove the original content and add the content from the platform POM:
+ root.children().toList().each { root.remove(it as Node) }
+ platformXml.asNode().children().each { root.append(it as Node) }
+
+ // Adjust the self artifact ID, as it should match the root module's coordinates:
+ ((root.get("artifactId") as NodeList).get(0) as Node).setValue(artifactId)
+
+ // Set packaging to POM to indicate that there's no artifact:
+ root.appendNode("packaging", "pom")
+
+ // Remove the original platform dependencies and add a single dependency on the platform module:
+ Node dependencies = (root.get("dependencies") as NodeList).get(0) as Node
+ dependencies.children().toList().each { dependencies.remove(it as Node) }
+ Node singleDependency = dependencies.appendNode("dependency")
+ singleDependency.appendNode("groupId", platformPublication.groupId)
+ singleDependency.appendNode("artifactId", platformPublication.artifactId)
+ singleDependency.appendNode("version", platformPublication.version)
+ singleDependency.appendNode("scope", "compile")
+ }
+ }
+
+ tasks.matching { it.name == "generatePomFileForKotlinMultiplatformPublication"}.configureEach {
+ dependsOn(tasks["generatePomFileFor${platformPublication.name.capitalize()}Publication"])
+ }
+ }
+} \ No newline at end of file
diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle
new file mode 100644
index 00000000..c16999ad
--- /dev/null
+++ b/gradle/publishing.gradle
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+// Configures publishing of Maven artifacts to MavenCentral
+
+apply plugin: 'maven-publish'
+apply plugin: 'signing'
+
+apply from: project.rootProject.file('gradle/maven-metadata.gradle')
+
+def isMultiplatform = project.name in ["kotlinx-serialization-core", "kotlinx-serialization-json","kotlinx-serialization-protobuf",
+ "kotlinx-serialization-cbor", "kotlinx-serialization-properties"]
+def isBom = project.name == "kotlinx-serialization-bom"
+
+if (!isBom) {
+ task stubSources(type: Jar) {
+ archiveClassifier = 'sources'
+ }
+
+ task stubJavadoc(type: Jar) {
+ archiveClassifier = 'javadoc'
+ }
+}
+
+task emptyJar(type: Jar) {
+}
+
+afterEvaluate {
+ task mainSourcesJar(type: Jar) {
+ classifier = 'sources'
+ if (isMultiplatform) {
+ from kotlin.sourceSets.commonMain.kotlin
+ } else {
+ from sourceSets.main.allSource
+ }
+ }
+}
+
+afterEvaluate {
+ publishing {
+ def variantName = "${project.name}"
+
+ if (!isMultiplatform && !isBom) {
+ publications {
+ maven(MavenPublication) { publication ->
+ artifactId variantName
+ publication.from components.java
+ publication.artifact mainSourcesJar
+ artifact stubJavadoc
+
+ PublishingKt.configureMavenCentralMetadata(publication.pom, project)
+ PublishingKt.signPublicationIfKeyPresent(project, publication)
+ }
+ }
+
+ return
+ }
+
+ // Rename artifacts for backward compatibility
+ publications.all {
+ def type = it.name
+ logger.info("Configuring $type")
+ switch (type) {
+ case 'kotlinMultiplatform':
+ // With Kotlin 1.4.0, the root module ID has no suffix, but for compatibility with
+ // the consumers who can't read Gradle module metadata, we publish the JVM artifacts in it
+ it.artifactId = variantName
+ apply from: "$rootDir/gradle/publish-mpp-root-module-in-platform.gradle"
+ publishPlatformArtifactsInRootModule(publications["jvm"])
+ break
+ case 'metadata':
+ case 'jvm':
+ case 'js':
+ it.artifactId = "$variantName-$type"
+ break
+ }
+ logger.info("Artifact id = ${it.artifactId}")
+
+ PublishingKt.configureMavenCentralMetadata(pom, project)
+ PublishingKt.signPublicationIfKeyPresent(project, it)
+
+ // The 'root' module publishes the JVM module's Javadoc JAR as per publishPlatformArtifactsInRootModule, and
+ // every other module should publish an empty Javadoc JAR. TODO: provide proper documentation artifacts?
+ if (name != "kotlinMultiplatform" && !isBom) {
+ artifact stubJavadoc
+ }
+ }
+ }
+}
+
+publishing {
+ repositories {
+ PublishingKt.configureMavenPublication(delegate, project)
+ }
+}
+
+// Compatibility with old TeamCity configurations that perform :kotlinx-coroutines-core:bintrayUpload
+task bintrayUpload(dependsOn: publish)
+
+// This is required for K/N publishing
+bintrayUpload.dependsOn publishToMavenLocal
+
diff --git a/gradle/teamcity.gradle b/gradle/teamcity.gradle
new file mode 100644
index 00000000..bb4cb575
--- /dev/null
+++ b/gradle/teamcity.gradle
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+def teamcitySuffix = project.findProperty("teamcitySuffix")?.toString()
+if (project.hasProperty("teamcity") && !build_snapshot_train) {
+ // Tell teamcity about version number
+ def postfix = (teamcitySuffix == null) ? "" : " ($teamcitySuffix)"
+ println("##teamcity[buildNumber '${project.version}${postfix}']")
+
+ gradle.taskGraph.beforeTask {
+ println("##teamcity[progressMessage 'Gradle: ${it.project.path}:${it.name}']")
+ }
+}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..7454180f
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..669386b8
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 00000000..1b6c7873
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000..ac1b06f9
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/guide/README.md b/guide/README.md
new file mode 100644
index 00000000..cd4d8e81
--- /dev/null
+++ b/guide/README.md
@@ -0,0 +1 @@
+Example files for the [Kotlin Serialization Guide](../docs/serialization-guide.md). \ No newline at end of file
diff --git a/guide/build.gradle b/guide/build.gradle
new file mode 100644
index 00000000..e01f660e
--- /dev/null
+++ b/guide/build.gradle
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+apply plugin: 'kotlin'
+apply plugin: 'kotlinx-serialization'
+
+dependencies {
+ testImplementation "org.jetbrains.kotlin:kotlin-test-junit"
+ testImplementation "org.jetbrains.kotlinx:kotlinx-knit-test:$knit_version"
+ testImplementation project(":kotlinx-serialization-core")
+ testImplementation project(":kotlinx-serialization-json")
+ testImplementation project(":kotlinx-serialization-cbor")
+ testImplementation project(":kotlinx-serialization-protobuf")
+ testImplementation project(":kotlinx-serialization-properties")
+}
+
+sourceSets.test {
+ java.srcDirs("example", "test")
+}
diff --git a/guide/example/example-basic-01.kt b/guide/example/example-basic-01.kt
new file mode 100644
index 00000000..7d0d17a9
--- /dev/null
+++ b/guide/example/example-basic-01.kt
@@ -0,0 +1,12 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleBasic01
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-basic-02.kt b/guide/example/example-basic-02.kt
new file mode 100644
index 00000000..3a5ebb94
--- /dev/null
+++ b/guide/example/example-basic-02.kt
@@ -0,0 +1,13 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleBasic02
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-basic-03.kt b/guide/example/example-basic-03.kt
new file mode 100644
index 00000000..f70369e5
--- /dev/null
+++ b/guide/example/example-basic-03.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleBasic03
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":"Kotlin"}
+ """)
+ println(data)
+}
diff --git a/guide/example/example-builtin-01.kt b/guide/example/example-builtin-01.kt
new file mode 100644
index 00000000..ee204449
--- /dev/null
+++ b/guide/example/example-builtin-01.kt
@@ -0,0 +1,18 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.exampleBuiltin01
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlin.math.*
+
+@Serializable
+class Data(
+ val answer: Int,
+ val pi: Double
+)
+
+fun main() {
+ val data = Data(42, PI)
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-builtin-02.kt b/guide/example/example-builtin-02.kt
new file mode 100644
index 00000000..351039cc
--- /dev/null
+++ b/guide/example/example-builtin-02.kt
@@ -0,0 +1,13 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.exampleBuiltin02
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Data(val signature: Long)
+
+fun main() {
+ val data = Data(0x1CAFE2FEED0BABE0)
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-builtin-03.kt b/guide/example/example-builtin-03.kt
new file mode 100644
index 00000000..211ca067
--- /dev/null
+++ b/guide/example/example-builtin-03.kt
@@ -0,0 +1,18 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.exampleBuiltin03
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.builtins.*
+
+@Serializable
+class Data(
+ @Serializable(with=LongAsStringSerializer::class)
+ val signature: Long
+)
+
+fun main() {
+ val data = Data(0x1CAFE2FEED0BABE0)
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-builtin-04.kt b/guide/example/example-builtin-04.kt
new file mode 100644
index 00000000..3ffa21fe
--- /dev/null
+++ b/guide/example/example-builtin-04.kt
@@ -0,0 +1,16 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.exampleBuiltin04
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+// The @Serializable annotation is not needed for enum classes
+enum class Status { SUPPORTED }
+
+@Serializable
+class Project(val name: String, val status: Status)
+
+fun main() {
+ val data = Project("kotlinx.serialization", Status.SUPPORTED)
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-builtin-05.kt b/guide/example/example-builtin-05.kt
new file mode 100644
index 00000000..148a335a
--- /dev/null
+++ b/guide/example/example-builtin-05.kt
@@ -0,0 +1,16 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.exampleBuiltin05
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable // required because of @SerialName
+enum class Status { @SerialName("maintained") SUPPORTED }
+
+@Serializable
+class Project(val name: String, val status: Status)
+
+fun main() {
+ val data = Project("kotlinx.serialization", Status.SUPPORTED)
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-builtin-06.kt b/guide/example/example-builtin-06.kt
new file mode 100644
index 00000000..00acfafb
--- /dev/null
+++ b/guide/example/example-builtin-06.kt
@@ -0,0 +1,13 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.exampleBuiltin06
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(val name: String)
+
+fun main() {
+ val pair = 1 to Project("kotlinx.serialization")
+ println(Json.encodeToString(pair))
+}
diff --git a/guide/example/example-builtin-07.kt b/guide/example/example-builtin-07.kt
new file mode 100644
index 00000000..e88b83f1
--- /dev/null
+++ b/guide/example/example-builtin-07.kt
@@ -0,0 +1,16 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.exampleBuiltin07
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(val name: String)
+
+fun main() {
+ val list = listOf(
+ Project("kotlinx.serialization"),
+ Project("kotlinx.coroutines")
+ )
+ println(Json.encodeToString(list))
+}
diff --git a/guide/example/example-builtin-08.kt b/guide/example/example-builtin-08.kt
new file mode 100644
index 00000000..93f18581
--- /dev/null
+++ b/guide/example/example-builtin-08.kt
@@ -0,0 +1,16 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.exampleBuiltin08
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(val name: String)
+
+fun main() {
+ val set = setOf(
+ Project("kotlinx.serialization"),
+ Project("kotlinx.coroutines")
+ )
+ println(Json.encodeToString(set))
+}
diff --git a/guide/example/example-builtin-09.kt b/guide/example/example-builtin-09.kt
new file mode 100644
index 00000000..90215282
--- /dev/null
+++ b/guide/example/example-builtin-09.kt
@@ -0,0 +1,21 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.exampleBuiltin09
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Data(
+ val a: List<Int>,
+ val b: Set<Int>
+)
+
+fun main() {
+ val data = Json.decodeFromString<Data>("""
+ {
+ "a": [42, 42],
+ "b": [42, 42]
+ }
+ """)
+ println(data)
+}
diff --git a/guide/example/example-builtin-10.kt b/guide/example/example-builtin-10.kt
new file mode 100644
index 00000000..07e6fca0
--- /dev/null
+++ b/guide/example/example-builtin-10.kt
@@ -0,0 +1,16 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.exampleBuiltin10
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(val name: String)
+
+fun main() {
+ val map = mapOf(
+ 1 to Project("kotlinx.serialization"),
+ 2 to Project("kotlinx.coroutines")
+ )
+ println(Json.encodeToString(map))
+}
diff --git a/guide/example/example-builtin-11.kt b/guide/example/example-builtin-11.kt
new file mode 100644
index 00000000..88950d87
--- /dev/null
+++ b/guide/example/example-builtin-11.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.exampleBuiltin11
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+object SerializationVersion {
+ val libraryVersion: String = "1.0.0"
+}
+
+fun main() {
+ println(Json.encodeToString(SerializationVersion))
+ println(Json.encodeToString(Unit))
+}
diff --git a/guide/example/example-classes-01.kt b/guide/example/example-classes-01.kt
new file mode 100644
index 00000000..0330f655
--- /dev/null
+++ b/guide/example/example-classes-01.kt
@@ -0,0 +1,23 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses01
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(
+ // name is a property with backing field -- serialized
+ var name: String
+) {
+ var stars: Int = 0 // property with a backing field -- serialized
+
+ val path: String // getter only, no backing field -- not serialized
+ get() = "kotlin/$name"
+
+ var id by ::name // delegated property -- not serialized
+}
+
+fun main() {
+ val data = Project("kotlinx.serialization").apply { stars = 9000 }
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-classes-02.kt b/guide/example/example-classes-02.kt
new file mode 100644
index 00000000..969cba9e
--- /dev/null
+++ b/guide/example/example-classes-02.kt
@@ -0,0 +1,20 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses02
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project private constructor(val owner: String, val name: String) {
+ constructor(path: String) : this(
+ owner = path.substringBefore('/'),
+ name = path.substringAfter('/')
+ )
+
+ val path: String
+ get() = "$owner/$name"
+}
+
+fun main() {
+ println(Json.encodeToString(Project("kotlin/kotlinx.serialization")))
+}
diff --git a/guide/example/example-classes-03.kt b/guide/example/example-classes-03.kt
new file mode 100644
index 00000000..cb99131b
--- /dev/null
+++ b/guide/example/example-classes-03.kt
@@ -0,0 +1,19 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses03
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(val name: String) {
+ init {
+ require(name.isNotEmpty()) { "name cannot be empty" }
+ }
+}
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":""}
+ """)
+ println(data)
+}
diff --git a/guide/example/example-classes-04.kt b/guide/example/example-classes-04.kt
new file mode 100644
index 00000000..5b691111
--- /dev/null
+++ b/guide/example/example-classes-04.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses04
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization"}
+ """)
+ println(data)
+}
diff --git a/guide/example/example-classes-05.kt b/guide/example/example-classes-05.kt
new file mode 100644
index 00000000..9143b261
--- /dev/null
+++ b/guide/example/example-classes-05.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses05
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(val name: String, val language: String = "Kotlin")
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization"}
+ """)
+ println(data)
+}
diff --git a/guide/example/example-classes-06.kt b/guide/example/example-classes-06.kt
new file mode 100644
index 00000000..c37b336d
--- /dev/null
+++ b/guide/example/example-classes-06.kt
@@ -0,0 +1,20 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses06
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+fun computeLanguage(): String {
+ println("Computing")
+ return "Kotlin"
+}
+
+@Serializable
+data class Project(val name: String, val language: String = computeLanguage())
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":"Kotlin"}
+ """)
+ println(data)
+}
diff --git a/guide/example/example-classes-07.kt b/guide/example/example-classes-07.kt
new file mode 100644
index 00000000..19aa153b
--- /dev/null
+++ b/guide/example/example-classes-07.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses07
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(val name: String, @Required val language: String = "Kotlin")
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization"}
+ """)
+ println(data)
+}
diff --git a/guide/example/example-classes-08.kt b/guide/example/example-classes-08.kt
new file mode 100644
index 00000000..d1cf638d
--- /dev/null
+++ b/guide/example/example-classes-08.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses08
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(val name: String, @Transient val language: String = "Kotlin")
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":"Kotlin"}
+ """)
+ println(data)
+}
diff --git a/guide/example/example-classes-09.kt b/guide/example/example-classes-09.kt
new file mode 100644
index 00000000..79231f70
--- /dev/null
+++ b/guide/example/example-classes-09.kt
@@ -0,0 +1,13 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses09
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(val name: String, val language: String = "Kotlin")
+
+fun main() {
+ val data = Project("kotlinx.serialization")
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-classes-10.kt b/guide/example/example-classes-10.kt
new file mode 100644
index 00000000..c5a1f739
--- /dev/null
+++ b/guide/example/example-classes-10.kt
@@ -0,0 +1,25 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses10
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(
+ val name: String,
+ @EncodeDefault val language: String = "Kotlin"
+)
+
+
+@Serializable
+data class User(
+ val name: String,
+ @EncodeDefault(EncodeDefault.Mode.NEVER) val projects: List<Project> = emptyList()
+)
+
+fun main() {
+ val userA = User("Alice", listOf(Project("kotlinx.serialization")))
+ val userB = User("Bob")
+ println(Json.encodeToString(userA))
+ println(Json.encodeToString(userB))
+}
diff --git a/guide/example/example-classes-11.kt b/guide/example/example-classes-11.kt
new file mode 100644
index 00000000..18a921b9
--- /dev/null
+++ b/guide/example/example-classes-11.kt
@@ -0,0 +1,13 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses11
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(val name: String, val renamedTo: String? = null)
+
+fun main() {
+ val data = Project("kotlinx.serialization")
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-classes-12.kt b/guide/example/example-classes-12.kt
new file mode 100644
index 00000000..232ee475
--- /dev/null
+++ b/guide/example/example-classes-12.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses12
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(val name: String, val language: String = "Kotlin")
+
+fun main() {
+ val data = Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":null}
+ """)
+ println(data)
+}
diff --git a/guide/example/example-classes-13.kt b/guide/example/example-classes-13.kt
new file mode 100644
index 00000000..4b93c0df
--- /dev/null
+++ b/guide/example/example-classes-13.kt
@@ -0,0 +1,17 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses13
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(val name: String, val owner: User)
+
+@Serializable
+class User(val name: String)
+
+fun main() {
+ val owner = User("kotlin")
+ val data = Project("kotlinx.serialization", owner)
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-classes-14.kt b/guide/example/example-classes-14.kt
new file mode 100644
index 00000000..3f2d7ce0
--- /dev/null
+++ b/guide/example/example-classes-14.kt
@@ -0,0 +1,17 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses14
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(val name: String, val owner: User, val maintainer: User)
+
+@Serializable
+class User(val name: String)
+
+fun main() {
+ val owner = User("kotlin")
+ val data = Project("kotlinx.serialization", owner, owner)
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-classes-15.kt b/guide/example/example-classes-15.kt
new file mode 100644
index 00000000..b259e0d7
--- /dev/null
+++ b/guide/example/example-classes-15.kt
@@ -0,0 +1,22 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses15
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Box<T>(val contents: T)
+
+@Serializable
+data class Project(val name: String, val language: String)
+
+@Serializable
+class Data(
+ val a: Box<Int>,
+ val b: Box<Project>
+)
+
+fun main() {
+ val data = Data(Box(42), Box(Project("kotlinx.serialization", "Kotlin")))
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-classes-16.kt b/guide/example/example-classes-16.kt
new file mode 100644
index 00000000..4a8b1972
--- /dev/null
+++ b/guide/example/example-classes-16.kt
@@ -0,0 +1,13 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.exampleClasses16
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(val name: String, @SerialName("lang") val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-formats-01.kt b/guide/example/example-formats-01.kt
new file mode 100644
index 00000000..ad2b3e6d
--- /dev/null
+++ b/guide/example/example-formats-01.kt
@@ -0,0 +1,21 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats01
+
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ val bytes = Cbor.encodeToByteArray(data)
+ println(bytes.toAsciiHexString())
+ val obj = Cbor.decodeFromByteArray<Project>(bytes)
+ println(obj)
+}
diff --git a/guide/example/example-formats-02.kt b/guide/example/example-formats-02.kt
new file mode 100644
index 00000000..c95e5329
--- /dev/null
+++ b/guide/example/example-formats-02.kt
@@ -0,0 +1,17 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats02
+
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.*
+
+val format = Cbor { ignoreUnknownKeys = true }
+
+@Serializable
+data class Project(val name: String)
+
+fun main() {
+ val data = format.decodeFromHexString<Project>(
+ "bf646e616d65756b6f746c696e782e73657269616c697a6174696f6e686c616e6775616765664b6f746c696eff"
+ )
+ println(data)
+}
diff --git a/guide/example/example-formats-03.kt b/guide/example/example-formats-03.kt
new file mode 100644
index 00000000..d2191f6a
--- /dev/null
+++ b/guide/example/example-formats-03.kt
@@ -0,0 +1,25 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats03
+
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+
+@Serializable
+data class Data(
+ @ByteString
+ val type2: ByteArray, // CBOR Major type 2
+ val type4: ByteArray // CBOR Major type 4
+)
+
+fun main() {
+ val data = Data(byteArrayOf(1, 2, 3, 4), byteArrayOf(5, 6, 7, 8))
+ val bytes = Cbor.encodeToByteArray(data)
+ println(bytes.toAsciiHexString())
+ val obj = Cbor.decodeFromByteArray<Data>(bytes)
+ println(obj)
+}
diff --git a/guide/example/example-formats-04.kt b/guide/example/example-formats-04.kt
new file mode 100644
index 00000000..9f6610bc
--- /dev/null
+++ b/guide/example/example-formats-04.kt
@@ -0,0 +1,21 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats04
+
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ val bytes = ProtoBuf.encodeToByteArray(data)
+ println(bytes.toAsciiHexString())
+ val obj = ProtoBuf.decodeFromByteArray<Project>(bytes)
+ println(obj)
+}
diff --git a/guide/example/example-formats-05.kt b/guide/example/example-formats-05.kt
new file mode 100644
index 00000000..796e3eb0
--- /dev/null
+++ b/guide/example/example-formats-05.kt
@@ -0,0 +1,26 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats05
+
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+
+@Serializable
+data class Project(
+ @ProtoNumber(1)
+ val name: String,
+ @ProtoNumber(3)
+ val language: String
+)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ val bytes = ProtoBuf.encodeToByteArray(data)
+ println(bytes.toAsciiHexString())
+ val obj = ProtoBuf.decodeFromByteArray<Project>(bytes)
+ println(obj)
+}
diff --git a/guide/example/example-formats-06.kt b/guide/example/example-formats-06.kt
new file mode 100644
index 00000000..5e4fbc50
--- /dev/null
+++ b/guide/example/example-formats-06.kt
@@ -0,0 +1,25 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats06
+
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+
+@Serializable
+class Data(
+ @ProtoType(ProtoIntegerType.DEFAULT)
+ val a: Int,
+ @ProtoType(ProtoIntegerType.SIGNED)
+ val b: Int,
+ @ProtoType(ProtoIntegerType.FIXED)
+ val c: Int
+)
+
+fun main() {
+ val data = Data(1, -2, 3)
+ println(ProtoBuf.encodeToByteArray(data).toAsciiHexString())
+}
diff --git a/guide/example/example-formats-07.kt b/guide/example/example-formats-07.kt
new file mode 100644
index 00000000..52bc826b
--- /dev/null
+++ b/guide/example/example-formats-07.kt
@@ -0,0 +1,23 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats07
+
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+
+@Serializable
+data class Data(
+ val a: List<Int> = emptyList(),
+ val b: List<Int> = emptyList()
+)
+
+fun main() {
+ val data = Data(listOf(1, 2, 3), listOf())
+ val bytes = ProtoBuf.encodeToByteArray(data)
+ println(bytes.toAsciiHexString())
+ println(ProtoBuf.decodeFromByteArray<Data>(bytes))
+}
diff --git a/guide/example/example-formats-08.kt b/guide/example/example-formats-08.kt
new file mode 100644
index 00000000..61d4aeb8
--- /dev/null
+++ b/guide/example/example-formats-08.kt
@@ -0,0 +1,18 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats08
+
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+import kotlinx.serialization.protobuf.schema.ProtoBufSchemaGenerator
+
+@Serializable
+data class SampleData(
+ val amount: Long,
+ val description: String?,
+ val department: String = "QA"
+)
+fun main() {
+ val descriptors = listOf(SampleData.serializer().descriptor)
+ val schemas = ProtoBufSchemaGenerator.generateSchemaText(descriptors)
+ println(schemas)
+}
diff --git a/guide/example/example-formats-09.kt b/guide/example/example-formats-09.kt
new file mode 100644
index 00000000..99ed45d2
--- /dev/null
+++ b/guide/example/example-formats-09.kt
@@ -0,0 +1,18 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats09
+
+import kotlinx.serialization.*
+import kotlinx.serialization.properties.Properties // todo: remove when no longer needed
+import kotlinx.serialization.properties.*
+
+@Serializable
+class Project(val name: String, val owner: User)
+
+@Serializable
+class User(val name: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", User("kotlin"))
+ val map = Properties.encodeToMap(data)
+ map.forEach { (k, v) -> println("$k = $v") }
+}
diff --git a/guide/example/example-formats-10.kt b/guide/example/example-formats-10.kt
new file mode 100644
index 00000000..9f83804d
--- /dev/null
+++ b/guide/example/example-formats-10.kt
@@ -0,0 +1,36 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats10
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+class ListEncoder : AbstractEncoder() {
+ val list = mutableListOf<Any>()
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun encodeValue(value: Any) {
+ list.add(value)
+ }
+}
+
+fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
+ val encoder = ListEncoder()
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.list
+}
+
+inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
+
+@Serializable
+data class Project(val name: String, val owner: User, val votes: Int)
+
+@Serializable
+data class User(val name: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", User("kotlin"), 9000)
+ println(encodeToList(data))
+}
diff --git a/guide/example/example-formats-11.kt b/guide/example/example-formats-11.kt
new file mode 100644
index 00000000..44bceb05
--- /dev/null
+++ b/guide/example/example-formats-11.kt
@@ -0,0 +1,62 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats11
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+class ListEncoder : AbstractEncoder() {
+ val list = mutableListOf<Any>()
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun encodeValue(value: Any) {
+ list.add(value)
+ }
+}
+
+fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
+ val encoder = ListEncoder()
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.list
+}
+
+inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
+
+class ListDecoder(val list: ArrayDeque<Any>) : AbstractDecoder() {
+ private var elementIndex = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun decodeValue(): Any = list.removeFirst()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == descriptor.elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ ListDecoder(list)
+}
+
+fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
+ val decoder = ListDecoder(ArrayDeque(list))
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
+
+@Serializable
+data class Project(val name: String, val owner: User, val votes: Int)
+
+@Serializable
+data class User(val name: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", User("kotlin"), 9000)
+ val list = encodeToList(data)
+ println(list)
+ val obj = decodeFromList<Project>(list)
+ println(obj)
+}
diff --git a/guide/example/example-formats-12.kt b/guide/example/example-formats-12.kt
new file mode 100644
index 00000000..ebedb302
--- /dev/null
+++ b/guide/example/example-formats-12.kt
@@ -0,0 +1,64 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats12
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+class ListEncoder : AbstractEncoder() {
+ val list = mutableListOf<Any>()
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun encodeValue(value: Any) {
+ list.add(value)
+ }
+}
+
+fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
+ val encoder = ListEncoder()
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.list
+}
+
+inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
+
+class ListDecoder(val list: ArrayDeque<Any>) : AbstractDecoder() {
+ private var elementIndex = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun decodeValue(): Any = list.removeFirst()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == descriptor.elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ ListDecoder(list)
+
+ override fun decodeSequentially(): Boolean = true
+}
+
+fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
+ val decoder = ListDecoder(ArrayDeque(list))
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
+
+@Serializable
+data class Project(val name: String, val owner: User, val votes: Int)
+
+@Serializable
+data class User(val name: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", User("kotlin"), 9000)
+ val list = encodeToList(data)
+ println(list)
+ val obj = decodeFromList<Project>(list)
+ println(obj)
+}
diff --git a/guide/example/example-formats-13.kt b/guide/example/example-formats-13.kt
new file mode 100644
index 00000000..e63c9b96
--- /dev/null
+++ b/guide/example/example-formats-13.kt
@@ -0,0 +1,72 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats13
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+class ListEncoder : AbstractEncoder() {
+ val list = mutableListOf<Any>()
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun encodeValue(value: Any) {
+ list.add(value)
+ }
+
+ override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
+ encodeInt(collectionSize)
+ return this
+ }
+}
+
+fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
+ val encoder = ListEncoder()
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.list
+}
+
+inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
+
+class ListDecoder(val list: ArrayDeque<Any>, var elementsCount: Int = 0) : AbstractDecoder() {
+ private var elementIndex = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun decodeValue(): Any = list.removeFirst()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ ListDecoder(list, descriptor.elementsCount)
+
+ override fun decodeSequentially(): Boolean = true
+
+ override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
+ decodeInt().also { elementsCount = it }
+}
+
+fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
+ val decoder = ListDecoder(ArrayDeque(list))
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
+
+@Serializable
+data class Project(val name: String, val owners: List<User>, val votes: Int)
+
+@Serializable
+data class User(val name: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", listOf(User("kotlin"), User("jetbrains")), 9000)
+ val list = encodeToList(data)
+ println(list)
+ val obj = decodeFromList<Project>(list)
+ println(obj)
+}
diff --git a/guide/example/example-formats-14.kt b/guide/example/example-formats-14.kt
new file mode 100644
index 00000000..0224916c
--- /dev/null
+++ b/guide/example/example-formats-14.kt
@@ -0,0 +1,78 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats14
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+class ListEncoder : AbstractEncoder() {
+ val list = mutableListOf<Any>()
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun encodeValue(value: Any) {
+ list.add(value)
+ }
+
+ override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
+ encodeInt(collectionSize)
+ return this
+ }
+
+ override fun encodeNull() = encodeValue("NULL")
+ override fun encodeNotNullMark() = encodeValue("!!")
+}
+
+fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
+ val encoder = ListEncoder()
+ encoder.encodeSerializableValue(serializer, value)
+ return encoder.list
+}
+
+inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
+
+class ListDecoder(val list: ArrayDeque<Any>, var elementsCount: Int = 0) : AbstractDecoder() {
+ private var elementIndex = 0
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun decodeValue(): Any = list.removeFirst()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ ListDecoder(list, descriptor.elementsCount)
+
+ override fun decodeSequentially(): Boolean = true
+
+ override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
+ decodeInt().also { elementsCount = it }
+
+ override fun decodeNotNullMark(): Boolean = decodeString() != "NULL"
+}
+
+fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
+ val decoder = ListDecoder(ArrayDeque(list))
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
+
+@Serializable
+data class Project(val name: String, val owner: User?, val votes: Int?)
+
+@Serializable
+data class User(val name: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", User("kotlin") , null)
+ val list = encodeToList(data)
+ println(list)
+ val obj = decodeFromList<Project>(list)
+ println(obj)
+}
+
diff --git a/guide/example/example-formats-15.kt b/guide/example/example-formats-15.kt
new file mode 100644
index 00000000..207daadf
--- /dev/null
+++ b/guide/example/example-formats-15.kt
@@ -0,0 +1,94 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats15
+
+import kotlinx.serialization.*
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import java.io.*
+
+class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() {
+ override val serializersModule: SerializersModule = EmptySerializersModule
+ override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
+ override fun encodeByte(value: Byte) = output.writeByte(value.toInt())
+ override fun encodeShort(value: Short) = output.writeShort(value.toInt())
+ override fun encodeInt(value: Int) = output.writeInt(value)
+ override fun encodeLong(value: Long) = output.writeLong(value)
+ override fun encodeFloat(value: Float) = output.writeFloat(value)
+ override fun encodeDouble(value: Double) = output.writeDouble(value)
+ override fun encodeChar(value: Char) = output.writeChar(value.code)
+ override fun encodeString(value: String) = output.writeUTF(value)
+ override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = output.writeInt(index)
+
+ override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
+ encodeInt(collectionSize)
+ return this
+ }
+
+ override fun encodeNull() = encodeBoolean(false)
+ override fun encodeNotNullMark() = encodeBoolean(true)
+}
+
+fun <T> encodeTo(output: DataOutput, serializer: SerializationStrategy<T>, value: T) {
+ val encoder = DataOutputEncoder(output)
+ encoder.encodeSerializableValue(serializer, value)
+}
+
+inline fun <reified T> encodeTo(output: DataOutput, value: T) = encodeTo(output, serializer(), value)
+
+class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() {
+ private var elementIndex = 0
+ override val serializersModule: SerializersModule = EmptySerializersModule
+ override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
+ override fun decodeByte(): Byte = input.readByte()
+ override fun decodeShort(): Short = input.readShort()
+ override fun decodeInt(): Int = input.readInt()
+ override fun decodeLong(): Long = input.readLong()
+ override fun decodeFloat(): Float = input.readFloat()
+ override fun decodeDouble(): Double = input.readDouble()
+ override fun decodeChar(): Char = input.readChar()
+ override fun decodeString(): String = input.readUTF()
+ override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = input.readInt()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ DataInputDecoder(input, descriptor.elementsCount)
+
+ override fun decodeSequentially(): Boolean = true
+
+ override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
+ decodeInt().also { elementsCount = it }
+
+ override fun decodeNotNullMark(): Boolean = decodeBoolean()
+}
+
+fun <T> decodeFrom(input: DataInput, deserializer: DeserializationStrategy<T>): T {
+ val decoder = DataInputDecoder(input)
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFrom(input: DataInput): T = decodeFrom(input, serializer())
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ val output = ByteArrayOutputStream()
+ encodeTo(DataOutputStream(output), data)
+ val bytes = output.toByteArray()
+ println(bytes.toAsciiHexString())
+ val input = ByteArrayInputStream(bytes)
+ val obj = decodeFrom<Project>(DataInputStream(input))
+ println(obj)
+}
diff --git a/guide/example/example-formats-16.kt b/guide/example/example-formats-16.kt
new file mode 100644
index 00000000..25d8662e
--- /dev/null
+++ b/guide/example/example-formats-16.kt
@@ -0,0 +1,135 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats16
+
+import kotlinx.serialization.*
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.encoding.*
+import java.io.*
+
+private val byteArraySerializer = serializer<ByteArray>()
+class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() {
+ override val serializersModule: SerializersModule = EmptySerializersModule
+ override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
+ override fun encodeByte(value: Byte) = output.writeByte(value.toInt())
+ override fun encodeShort(value: Short) = output.writeShort(value.toInt())
+ override fun encodeInt(value: Int) = output.writeInt(value)
+ override fun encodeLong(value: Long) = output.writeLong(value)
+ override fun encodeFloat(value: Float) = output.writeFloat(value)
+ override fun encodeDouble(value: Double) = output.writeDouble(value)
+ override fun encodeChar(value: Char) = output.writeChar(value.code)
+ override fun encodeString(value: String) = output.writeUTF(value)
+ override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = output.writeInt(index)
+
+ override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
+ encodeInt(collectionSize)
+ return this
+ }
+
+ override fun encodeNull() = encodeBoolean(false)
+ override fun encodeNotNullMark() = encodeBoolean(true)
+
+ override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
+ if (serializer.descriptor == byteArraySerializer.descriptor)
+ encodeByteArray(value as ByteArray)
+ else
+ super.encodeSerializableValue(serializer, value)
+ }
+
+ private fun encodeByteArray(bytes: ByteArray) {
+ encodeCompactSize(bytes.size)
+ output.write(bytes)
+ }
+
+ private fun encodeCompactSize(value: Int) {
+ if (value < 0xff) {
+ output.writeByte(value)
+ } else {
+ output.writeByte(0xff)
+ output.writeInt(value)
+ }
+ }
+}
+
+fun <T> encodeTo(output: DataOutput, serializer: SerializationStrategy<T>, value: T) {
+ val encoder = DataOutputEncoder(output)
+ encoder.encodeSerializableValue(serializer, value)
+}
+
+inline fun <reified T> encodeTo(output: DataOutput, value: T) = encodeTo(output, serializer(), value)
+
+class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() {
+ private var elementIndex = 0
+ override val serializersModule: SerializersModule = EmptySerializersModule
+ override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
+ override fun decodeByte(): Byte = input.readByte()
+ override fun decodeShort(): Short = input.readShort()
+ override fun decodeInt(): Int = input.readInt()
+ override fun decodeLong(): Long = input.readLong()
+ override fun decodeFloat(): Float = input.readFloat()
+ override fun decodeDouble(): Double = input.readDouble()
+ override fun decodeChar(): Char = input.readChar()
+ override fun decodeString(): String = input.readUTF()
+ override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = input.readInt()
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
+ return elementIndex++
+ }
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ DataInputDecoder(input, descriptor.elementsCount)
+
+ override fun decodeSequentially(): Boolean = true
+
+ override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
+ decodeInt().also { elementsCount = it }
+
+ override fun decodeNotNullMark(): Boolean = decodeBoolean()
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>, previousValue: T?): T =
+ if (deserializer.descriptor == byteArraySerializer.descriptor)
+ decodeByteArray() as T
+ else
+ super.decodeSerializableValue(deserializer, previousValue)
+
+ private fun decodeByteArray(): ByteArray {
+ val bytes = ByteArray(decodeCompactSize())
+ input.readFully(bytes)
+ return bytes
+ }
+
+ private fun decodeCompactSize(): Int {
+ val byte = input.readByte().toInt() and 0xff
+ if (byte < 0xff) return byte
+ return input.readInt()
+ }
+}
+
+fun <T> decodeFrom(input: DataInput, deserializer: DeserializationStrategy<T>): T {
+ val decoder = DataInputDecoder(input)
+ return decoder.decodeSerializableValue(deserializer)
+}
+
+inline fun <reified T> decodeFrom(input: DataInput): T = decodeFrom(input, serializer())
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+ if (it in 32..127) it.toInt().toChar().toString() else
+ "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+
+@Serializable
+data class Project(val name: String, val attachment: ByteArray)
+
+fun main() {
+ val data = Project("kotlinx.serialization", byteArrayOf(0x0A, 0x0B, 0x0C, 0x0D))
+ val output = ByteArrayOutputStream()
+ encodeTo(DataOutputStream(output), data)
+ val bytes = output.toByteArray()
+ println(bytes.toAsciiHexString())
+ val input = ByteArrayInputStream(bytes)
+ val obj = decodeFrom<Project>(DataInputStream(input))
+ println(obj)
+}
diff --git a/guide/example/example-json-01.kt b/guide/example/example-json-01.kt
new file mode 100644
index 00000000..d3fb2671
--- /dev/null
+++ b/guide/example/example-json-01.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson01
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+val format = Json { prettyPrint = true }
+
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-json-02.kt b/guide/example/example-json-02.kt
new file mode 100644
index 00000000..6eda7624
--- /dev/null
+++ b/guide/example/example-json-02.kt
@@ -0,0 +1,23 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson02
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+val format = Json { isLenient = true }
+
+enum class Status { SUPPORTED }
+
+@Serializable
+data class Project(val name: String, val status: Status, val votes: Int)
+
+fun main() {
+ val data = format.decodeFromString<Project>("""
+ {
+ name : kotlinx.serialization,
+ status : SUPPORTED,
+ votes : "9000"
+ }
+ """)
+ println(data)
+}
diff --git a/guide/example/example-json-03.kt b/guide/example/example-json-03.kt
new file mode 100644
index 00000000..77f0ae24
--- /dev/null
+++ b/guide/example/example-json-03.kt
@@ -0,0 +1,17 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson03
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+val format = Json { ignoreUnknownKeys = true }
+
+@Serializable
+data class Project(val name: String)
+
+fun main() {
+ val data = format.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":"Kotlin"}
+ """)
+ println(data)
+}
diff --git a/guide/example/example-json-04.kt b/guide/example/example-json-04.kt
new file mode 100644
index 00000000..a8ae148c
--- /dev/null
+++ b/guide/example/example-json-04.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson04
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(@JsonNames("title") val name: String)
+
+fun main() {
+ val project = Json.decodeFromString<Project>("""{"name":"kotlinx.serialization"}""")
+ println(project)
+ val oldProject = Json.decodeFromString<Project>("""{"title":"kotlinx.coroutines"}""")
+ println(oldProject)
+}
diff --git a/guide/example/example-json-05.kt b/guide/example/example-json-05.kt
new file mode 100644
index 00000000..e1b54225
--- /dev/null
+++ b/guide/example/example-json-05.kt
@@ -0,0 +1,17 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson05
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+val format = Json { coerceInputValues = true }
+
+@Serializable
+data class Project(val name: String, val language: String = "Kotlin")
+
+fun main() {
+ val data = format.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","language":null}
+ """)
+ println(data)
+}
diff --git a/guide/example/example-json-06.kt b/guide/example/example-json-06.kt
new file mode 100644
index 00000000..605b4884
--- /dev/null
+++ b/guide/example/example-json-06.kt
@@ -0,0 +1,19 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson06
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+val format = Json { encodeDefaults = true }
+
+@Serializable
+class Project(
+ val name: String,
+ val language: String = "Kotlin",
+ val website: String? = null
+)
+
+fun main() {
+ val data = Project("kotlinx.serialization")
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-json-07.kt b/guide/example/example-json-07.kt
new file mode 100644
index 00000000..60aa2b28
--- /dev/null
+++ b/guide/example/example-json-07.kt
@@ -0,0 +1,23 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson07
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+val format = Json { explicitNulls = false }
+
+@Serializable
+data class Project(
+ val name: String,
+ val language: String,
+ val version: String? = "1.2.2",
+ val website: String?,
+ val description: String? = null
+)
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin", null, null, null)
+ val json = format.encodeToString(data)
+ println(json)
+ println(format.decodeFromString<Project>(json))
+}
diff --git a/guide/example/example-json-08.kt b/guide/example/example-json-08.kt
new file mode 100644
index 00000000..86e6298f
--- /dev/null
+++ b/guide/example/example-json-08.kt
@@ -0,0 +1,18 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson08
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+val format = Json { allowStructuredMapKeys = true }
+
+@Serializable
+data class Project(val name: String)
+
+fun main() {
+ val map = mapOf(
+ Project("kotlinx.serialization") to "Serialization",
+ Project("kotlinx.coroutines") to "Coroutines"
+ )
+ println(format.encodeToString(map))
+}
diff --git a/guide/example/example-json-09.kt b/guide/example/example-json-09.kt
new file mode 100644
index 00000000..1303fdd7
--- /dev/null
+++ b/guide/example/example-json-09.kt
@@ -0,0 +1,17 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson09
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+val format = Json { allowSpecialFloatingPointValues = true }
+
+@Serializable
+class Data(
+ val value: Double
+)
+
+fun main() {
+ val data = Data(Double.NaN)
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-json-10.kt b/guide/example/example-json-10.kt
new file mode 100644
index 00000000..49df395e
--- /dev/null
+++ b/guide/example/example-json-10.kt
@@ -0,0 +1,21 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson10
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+val format = Json { classDiscriminator = "#class" }
+
+@Serializable
+sealed class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-json-11.kt b/guide/example/example-json-11.kt
new file mode 100644
index 00000000..57e350ad
--- /dev/null
+++ b/guide/example/example-json-11.kt
@@ -0,0 +1,31 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson11
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+@JsonClassDiscriminator("message_type")
+sealed class Base
+
+@Serializable // Class discriminator is inherited from Base
+sealed class ErrorClass: Base()
+
+@Serializable
+data class Message(val message: Base, val error: ErrorClass?)
+
+@Serializable
+@SerialName("my.app.BaseMessage")
+data class BaseMessage(val message: String) : Base()
+
+@Serializable
+@SerialName("my.app.GenericError")
+data class GenericError(@SerialName("error_code") val errorCode: Int) : ErrorClass()
+
+
+val format = Json { classDiscriminator = "#class" }
+
+fun main() {
+ val data = Message(BaseMessage("not found"), GenericError(404))
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-json-12.kt b/guide/example/example-json-12.kt
new file mode 100644
index 00000000..cc98bf5c
--- /dev/null
+++ b/guide/example/example-json-12.kt
@@ -0,0 +1,12 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson12
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+fun main() {
+ val element = Json.parseToJsonElement("""
+ {"name":"kotlinx.serialization","language":"Kotlin"}
+ """)
+ println(element)
+}
diff --git a/guide/example/example-json-13.kt b/guide/example/example-json-13.kt
new file mode 100644
index 00000000..97188ff5
--- /dev/null
+++ b/guide/example/example-json-13.kt
@@ -0,0 +1,18 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson13
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+fun main() {
+ val element = Json.parseToJsonElement("""
+ {
+ "name": "kotlinx.serialization",
+ "forks": [{"votes": 42}, {"votes": 9000}, {}]
+ }
+ """)
+ val sum = element
+ .jsonObject["forks"]!!
+ .jsonArray.sumOf { it.jsonObject["votes"]?.jsonPrimitive?.int ?: 0 }
+ println(sum)
+}
diff --git a/guide/example/example-json-14.kt b/guide/example/example-json-14.kt
new file mode 100644
index 00000000..0e5ba362
--- /dev/null
+++ b/guide/example/example-json-14.kt
@@ -0,0 +1,23 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson14
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+fun main() {
+ val element = buildJsonObject {
+ put("name", "kotlinx.serialization")
+ putJsonObject("owner") {
+ put("name", "kotlin")
+ }
+ putJsonArray("forks") {
+ addJsonObject {
+ put("votes", 42)
+ }
+ addJsonObject {
+ put("votes", 9000)
+ }
+ }
+ }
+ println(element)
+}
diff --git a/guide/example/example-json-15.kt b/guide/example/example-json-15.kt
new file mode 100644
index 00000000..0aa317f4
--- /dev/null
+++ b/guide/example/example-json-15.kt
@@ -0,0 +1,17 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson15
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ val element = buildJsonObject {
+ put("name", "kotlinx.serialization")
+ put("language", "Kotlin")
+ }
+ val data = Json.decodeFromJsonElement<Project>(element)
+ println(data)
+}
diff --git a/guide/example/example-json-16.kt b/guide/example/example-json-16.kt
new file mode 100644
index 00000000..b66d3ac2
--- /dev/null
+++ b/guide/example/example-json-16.kt
@@ -0,0 +1,32 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson16
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.builtins.*
+
+@Serializable
+data class Project(
+ val name: String,
+ @Serializable(with = UserListSerializer::class)
+ val users: List<User>
+)
+
+@Serializable
+data class User(val name: String)
+
+object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) {
+ // If response is not an array, then it is a single object that should be wrapped into the array
+ override fun transformDeserialize(element: JsonElement): JsonElement =
+ if (element !is JsonArray) JsonArray(listOf(element)) else element
+}
+
+fun main() {
+ println(Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","users":{"name":"kotlin"}}
+ """))
+ println(Json.decodeFromString<Project>("""
+ {"name":"kotlinx.serialization","users":[{"name":"kotlin"},{"name":"jetbrains"}]}
+ """))
+}
diff --git a/guide/example/example-json-17.kt b/guide/example/example-json-17.kt
new file mode 100644
index 00000000..7b1b88f3
--- /dev/null
+++ b/guide/example/example-json-17.kt
@@ -0,0 +1,30 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson17
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.builtins.*
+
+@Serializable
+data class Project(
+ val name: String,
+ @Serializable(with = UserListSerializer::class)
+ val users: List<User>
+)
+
+@Serializable
+data class User(val name: String)
+
+object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) {
+
+ override fun transformSerialize(element: JsonElement): JsonElement {
+ require(element is JsonArray) // this serializer is used only with lists
+ return element.singleOrNull() ?: element
+ }
+}
+
+fun main() {
+ val data = Project("kotlinx.serialization", listOf(User("kotlin")))
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-json-18.kt b/guide/example/example-json-18.kt
new file mode 100644
index 00000000..d3da62d3
--- /dev/null
+++ b/guide/example/example-json-18.kt
@@ -0,0 +1,22 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson18
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Project(val name: String, val language: String)
+
+object ProjectSerializer : JsonTransformingSerializer<Project>(Project.serializer()) {
+ override fun transformSerialize(element: JsonElement): JsonElement =
+ // Filter out top-level key value pair with the key "language" and the value "Kotlin"
+ JsonObject(element.jsonObject.filterNot {
+ (k, v) -> k == "language" && v.jsonPrimitive.content == "Kotlin"
+ })
+}
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(Json.encodeToString(data)) // using plugin-generated serializer
+ println(Json.encodeToString(ProjectSerializer, data)) // using custom serializer
+}
diff --git a/guide/example/example-json-19.kt b/guide/example/example-json-19.kt
new file mode 100644
index 00000000..4455d637
--- /dev/null
+++ b/guide/example/example-json-19.kt
@@ -0,0 +1,36 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson19
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.builtins.*
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+data class BasicProject(override val name: String): Project()
+
+
+@Serializable
+data class OwnedProject(override val name: String, val owner: String) : Project()
+
+object ProjectSerializer : JsonContentPolymorphicSerializer<Project>(Project::class) {
+ override fun selectDeserializer(element: JsonElement) = when {
+ "owner" in element.jsonObject -> OwnedProject.serializer()
+ else -> BasicProject.serializer()
+ }
+}
+
+fun main() {
+ val data = listOf(
+ OwnedProject("kotlinx.serialization", "kotlin"),
+ BasicProject("example")
+ )
+ val string = Json.encodeToString(ListSerializer(ProjectSerializer), data)
+ println(string)
+ println(Json.decodeFromString(ListSerializer(ProjectSerializer), string))
+}
diff --git a/guide/example/example-json-20.kt b/guide/example/example-json-20.kt
new file mode 100644
index 00000000..e613a08f
--- /dev/null
+++ b/guide/example/example-json-20.kt
@@ -0,0 +1,59 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson20
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+@Serializable(with = ResponseSerializer::class)
+sealed class Response<out T> {
+ data class Ok<out T>(val data: T) : Response<T>()
+ data class Error(val message: String) : Response<Nothing>()
+}
+
+class ResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Response<T>> {
+ override val descriptor: SerialDescriptor = buildSerialDescriptor("Response", PolymorphicKind.SEALED) {
+ element("Ok", buildClassSerialDescriptor("Ok") {
+ element<String>("message")
+ })
+ element("Error", dataSerializer.descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): Response<T> {
+ // Decoder -> JsonDecoder
+ require(decoder is JsonDecoder) // this class can be decoded only by Json
+ // JsonDecoder -> JsonElement
+ val element = decoder.decodeJsonElement()
+ // JsonElement -> value
+ if (element is JsonObject && "error" in element)
+ return Response.Error(element["error"]!!.jsonPrimitive.content)
+ return Response.Ok(decoder.json.decodeFromJsonElement(dataSerializer, element))
+ }
+
+ override fun serialize(encoder: Encoder, value: Response<T>) {
+ // Encoder -> JsonEncoder
+ require(encoder is JsonEncoder) // This class can be encoded only by Json
+ // value -> JsonElement
+ val element = when (value) {
+ is Response.Ok -> encoder.json.encodeToJsonElement(dataSerializer, value.data)
+ is Response.Error -> buildJsonObject { put("error", value.message) }
+ }
+ // JsonElement -> JsonEncoder
+ encoder.encodeJsonElement(element)
+ }
+}
+
+@Serializable
+data class Project(val name: String)
+
+fun main() {
+ val responses = listOf(
+ Response.Ok(Project("kotlinx.serialization")),
+ Response.Error("Not found")
+ )
+ val string = Json.encodeToString(responses)
+ println(string)
+ println(Json.decodeFromString<List<Response<Project>>>(string))
+}
diff --git a/guide/example/example-json-21.kt b/guide/example/example-json-21.kt
new file mode 100644
index 00000000..92de429b
--- /dev/null
+++ b/guide/example/example-json-21.kt
@@ -0,0 +1,37 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson21
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+data class UnknownProject(val name: String, val details: JsonObject)
+
+object UnknownProjectSerializer : KSerializer<UnknownProject> {
+ override val descriptor: SerialDescriptor = buildClassSerialDescriptor("UnknownProject") {
+ element<String>("name")
+ element<JsonElement>("details")
+ }
+
+ override fun deserialize(decoder: Decoder): UnknownProject {
+ // Cast to JSON-specific interface
+ val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON")
+ // Read the whole content as JSON
+ val json = jsonInput.decodeJsonElement().jsonObject
+ // Extract and remove name property
+ val name = json.getValue("name").jsonPrimitive.content
+ val details = json.toMutableMap()
+ details.remove("name")
+ return UnknownProject(name, JsonObject(details))
+ }
+
+ override fun serialize(encoder: Encoder, value: UnknownProject) {
+ error("Serialization is not supported")
+ }
+}
+
+fun main() {
+ println(Json.decodeFromString(UnknownProjectSerializer, """{"type":"unknown","name":"example","maintainer":"Unknown","license":"Apache 2.0"}"""))
+}
diff --git a/guide/example/example-poly-01.kt b/guide/example/example-poly-01.kt
new file mode 100644
index 00000000..80dbab15
--- /dev/null
+++ b/guide/example/example-poly-01.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly01
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+open class Project(val name: String)
+
+class OwnedProject(name: String, val owner: String) : Project(name)
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-poly-02.kt b/guide/example/example-poly-02.kt
new file mode 100644
index 00000000..a5bc2f9d
--- /dev/null
+++ b/guide/example/example-poly-02.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly02
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+open class Project(val name: String)
+
+class OwnedProject(name: String, val owner: String) : Project(name)
+
+fun main() {
+ val data = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-poly-03.kt b/guide/example/example-poly-03.kt
new file mode 100644
index 00000000..d39cabd6
--- /dev/null
+++ b/guide/example/example-poly-03.kt
@@ -0,0 +1,17 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly03
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-poly-04.kt b/guide/example/example-poly-04.kt
new file mode 100644
index 00000000..1cef6aa0
--- /dev/null
+++ b/guide/example/example-poly-04.kt
@@ -0,0 +1,18 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly04
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+sealed class Project {
+ abstract val name: String
+}
+
+@Serializable
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(Json.encodeToString(data)) // Serializing data of compile-time type Project
+}
diff --git a/guide/example/example-poly-05.kt b/guide/example/example-poly-05.kt
new file mode 100644
index 00000000..48724d00
--- /dev/null
+++ b/guide/example/example-poly-05.kt
@@ -0,0 +1,18 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly05
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+sealed class Project {
+ abstract val name: String
+}
+
+@Serializable
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data = OwnedProject("kotlinx.coroutines", "kotlin") // data: OwnedProject here
+ println(Json.encodeToString(data)) // Serializing data of compile-time type OwnedProject
+}
diff --git a/guide/example/example-poly-06.kt b/guide/example/example-poly-06.kt
new file mode 100644
index 00000000..c54136c6
--- /dev/null
+++ b/guide/example/example-poly-06.kt
@@ -0,0 +1,19 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly06
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+sealed class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-poly-07.kt b/guide/example/example-poly-07.kt
new file mode 100644
index 00000000..7edd231c
--- /dev/null
+++ b/guide/example/example-poly-07.kt
@@ -0,0 +1,21 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly07
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+sealed class Project {
+ abstract val name: String
+ var status = "open"
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val json = Json { encodeDefaults = true } // "status" will be skipped otherwise
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(json.encodeToString(data))
+}
diff --git a/guide/example/example-poly-08.kt b/guide/example/example-poly-08.kt
new file mode 100644
index 00000000..ab99810c
--- /dev/null
+++ b/guide/example/example-poly-08.kt
@@ -0,0 +1,19 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly08
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+sealed class Response
+
+@Serializable
+object EmptyResponse : Response()
+
+@Serializable
+class TextResponse(val text: String) : Response()
+
+fun main() {
+ val list = listOf(EmptyResponse, TextResponse("OK"))
+ println(Json.encodeToString(list))
+}
diff --git a/guide/example/example-poly-09.kt b/guide/example/example-poly-09.kt
new file mode 100644
index 00000000..bc52c33d
--- /dev/null
+++ b/guide/example/example-poly-09.kt
@@ -0,0 +1,29 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly09
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-poly-10.kt b/guide/example/example-poly-10.kt
new file mode 100644
index 00000000..4a4bedbe
--- /dev/null
+++ b/guide/example/example-poly-10.kt
@@ -0,0 +1,28 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly10
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+interface Project {
+ val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project
+
+fun main() {
+ val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-poly-11.kt b/guide/example/example-poly-11.kt
new file mode 100644
index 00000000..30c3c4d7
--- /dev/null
+++ b/guide/example/example-poly-11.kt
@@ -0,0 +1,31 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly11
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+interface Project {
+ val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project
+
+@Serializable
+class Data(val project: Project) // Project is an interface
+
+fun main() {
+ val data = Data(OwnedProject("kotlinx.coroutines", "kotlin"))
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-poly-12.kt b/guide/example/example-poly-12.kt
new file mode 100644
index 00000000..5006cb4d
--- /dev/null
+++ b/guide/example/example-poly-12.kt
@@ -0,0 +1,29 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly12
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Any = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-poly-13.kt b/guide/example/example-poly-13.kt
new file mode 100644
index 00000000..e79de66f
--- /dev/null
+++ b/guide/example/example-poly-13.kt
@@ -0,0 +1,28 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly13
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(OwnedProject::class)
+ }
+}
+val format = Json { serializersModule = module }
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Any = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-poly-14.kt b/guide/example/example-poly-14.kt
new file mode 100644
index 00000000..eea74517
--- /dev/null
+++ b/guide/example/example-poly-14.kt
@@ -0,0 +1,29 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly14
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
+fun main() {
+ val data: Any = OwnedProject("kotlinx.coroutines", "kotlin")
+ println(format.encodeToString(PolymorphicSerializer(Any::class), data))
+}
diff --git a/guide/example/example-poly-15.kt b/guide/example/example-poly-15.kt
new file mode 100644
index 00000000..e191b8f6
--- /dev/null
+++ b/guide/example/example-poly-15.kt
@@ -0,0 +1,34 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly15
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.modules.*
+
+val module = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+interface Project {
+ val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project
+
+@Serializable
+class Data(
+ @Polymorphic // the code does not compile without it
+ val project: Any
+)
+
+fun main() {
+ val data = Data(OwnedProject("kotlinx.coroutines", "kotlin"))
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-poly-16.kt b/guide/example/example-poly-16.kt
new file mode 100644
index 00000000..079dc70c
--- /dev/null
+++ b/guide/example/example-poly-16.kt
@@ -0,0 +1,38 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly16
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.modules.*
+import kotlin.reflect.KClass
+
+val module = SerializersModule {
+ fun PolymorphicModuleBuilder<Project>.registerProjectSubclasses() {
+ subclass(OwnedProject::class)
+ }
+ polymorphic(Any::class) { registerProjectSubclasses() }
+ polymorphic(Project::class) { registerProjectSubclasses() }
+}
+
+val format = Json { serializersModule = module }
+
+interface Project {
+ val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project
+
+@Serializable
+class Data(
+ val project: Project,
+ @Polymorphic val any: Any
+)
+
+fun main() {
+ val project = OwnedProject("kotlinx.coroutines", "kotlin")
+ val data = Data(project, project)
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-poly-17.kt b/guide/example/example-poly-17.kt
new file mode 100644
index 00000000..0b15a159
--- /dev/null
+++ b/guide/example/example-poly-17.kt
@@ -0,0 +1,48 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly17
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.modules.*
+
+@Serializable
+abstract class Response<out T>
+
+@Serializable
+@SerialName("OkResponse")
+data class OkResponse<out T>(val data: T) : Response<T>()
+
+val responseModule = SerializersModule {
+ polymorphic(Response::class) {
+ subclass(OkResponse.serializer(PolymorphicSerializer(Any::class)))
+ }
+}
+
+val projectModule = SerializersModule {
+ fun PolymorphicModuleBuilder<Project>.registerProjectSubclasses() {
+ subclass(OwnedProject::class)
+ }
+ polymorphic(Any::class) { registerProjectSubclasses() }
+ polymorphic(Project::class) { registerProjectSubclasses() }
+}
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("OwnedProject")
+data class OwnedProject(override val name: String, val owner: String) : Project()
+
+val format = Json { serializersModule = projectModule + responseModule }
+
+fun main() {
+ // both Response and Project are abstract and their concrete subtypes are being serialized
+ val data: Response<Project> = OkResponse(OwnedProject("kotlinx.serialization", "kotlin"))
+ val string = format.encodeToString(data)
+ println(string)
+ println(format.decodeFromString<Response<Project>>(string))
+}
+
diff --git a/guide/example/example-poly-18.kt b/guide/example/example-poly-18.kt
new file mode 100644
index 00000000..18ac3ec6
--- /dev/null
+++ b/guide/example/example-poly-18.kt
@@ -0,0 +1,30 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly18
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.modules.*
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+@SerialName("OwnedProject")
+data class OwnedProject(override val name: String, val owner: String) : Project()
+
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ }
+}
+
+val format = Json { serializersModule = module }
+
+fun main() {
+ println(format.decodeFromString<Project>("""
+ {"type":"unknown","name":"example"}
+ """))
+}
diff --git a/guide/example/example-poly-19.kt b/guide/example/example-poly-19.kt
new file mode 100644
index 00000000..83677ebe
--- /dev/null
+++ b/guide/example/example-poly-19.kt
@@ -0,0 +1,37 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly19
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.modules.*
+
+@Serializable
+abstract class Project {
+ abstract val name: String
+}
+
+@Serializable
+data class BasicProject(override val name: String, val type: String): Project()
+
+@Serializable
+@SerialName("OwnedProject")
+data class OwnedProject(override val name: String, val owner: String) : Project()
+
+val module = SerializersModule {
+ polymorphic(Project::class) {
+ subclass(OwnedProject::class)
+ defaultDeserializer { BasicProject.serializer() }
+ }
+}
+
+val format = Json { serializersModule = module }
+
+fun main() {
+ println(format.decodeFromString<List<Project>>("""
+ [
+ {"type":"unknown","name":"example"},
+ {"type":"OwnedProject","name":"kotlinx.serialization","owner":"kotlin"}
+ ]
+ """))
+}
diff --git a/guide/example/example-poly-20.kt b/guide/example/example-poly-20.kt
new file mode 100644
index 00000000..b597fbeb
--- /dev/null
+++ b/guide/example/example-poly-20.kt
@@ -0,0 +1,74 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.examplePoly20
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+interface Animal {
+}
+
+interface Cat : Animal {
+ val catType: String
+}
+
+interface Dog : Animal {
+ val dogType: String
+}
+
+private class CatImpl : Cat {
+ override val catType: String = "Tabby"
+}
+
+private class DogImpl : Dog {
+ override val dogType: String = "Husky"
+}
+
+object AnimalProvider {
+ fun createCat(): Cat = CatImpl()
+ fun createDog(): Dog = DogImpl()
+}
+
+val module = SerializersModule {
+ polymorphicDefaultSerializer(Animal::class) { instance ->
+ @Suppress("UNCHECKED_CAST")
+ when (instance) {
+ is Cat -> CatSerializer as SerializationStrategy<Animal>
+ is Dog -> DogSerializer as SerializationStrategy<Animal>
+ else -> null
+ }
+ }
+}
+
+object CatSerializer : SerializationStrategy<Cat> {
+ override val descriptor = buildClassSerialDescriptor("Cat") {
+ element<String>("catType")
+ }
+
+ override fun serialize(encoder: Encoder, value: Cat) {
+ encoder.encodeStructure(descriptor) {
+ encodeStringElement(descriptor, 0, value.catType)
+ }
+ }
+}
+
+object DogSerializer : SerializationStrategy<Dog> {
+ override val descriptor = buildClassSerialDescriptor("Dog") {
+ element<String>("dogType")
+ }
+
+ override fun serialize(encoder: Encoder, value: Dog) {
+ encoder.encodeStructure(descriptor) {
+ encodeStringElement(descriptor, 0, value.dogType)
+ }
+ }
+}
+
+val format = Json { serializersModule = module }
+
+fun main() {
+ println(format.encodeToString<Animal>(AnimalProvider.createCat()))
+}
diff --git a/guide/example/example-readme-01.kt b/guide/example/example-readme-01.kt
new file mode 100644
index 00000000..c241188f
--- /dev/null
+++ b/guide/example/example-readme-01.kt
@@ -0,0 +1,18 @@
+// This file was automatically generated from README.md by Knit tool. Do not edit.
+package example.exampleReadme01
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+data class Project(val name: String, val language: String)
+
+fun main() {
+ // Serializing objects
+ val data = Project("kotlinx.serialization", "Kotlin")
+ val string = Json.encodeToString(data)
+ println(string) // {"name":"kotlinx.serialization","language":"Kotlin"}
+ // Deserializing back into objects
+ val obj = Json.decodeFromString<Project>(string)
+ println(obj) // Project(name=kotlinx.serialization, language=Kotlin)
+}
diff --git a/guide/example/example-serializer-01.kt b/guide/example/example-serializer-01.kt
new file mode 100644
index 00000000..13aaa6a6
--- /dev/null
+++ b/guide/example/example-serializer-01.kt
@@ -0,0 +1,13 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer01
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+class Color(val rgb: Int)
+
+fun main() {
+ val green = Color(0x00ff00)
+ println(Json.encodeToString(green))
+}
diff --git a/guide/example/example-serializer-02.kt b/guide/example/example-serializer-02.kt
new file mode 100644
index 00000000..98bda6dc
--- /dev/null
+++ b/guide/example/example-serializer-02.kt
@@ -0,0 +1,13 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer02
+
+import kotlinx.serialization.*
+
+@Serializable
+@SerialName("Color")
+class Color(val rgb: Int)
+
+fun main() {
+ val colorSerializer: KSerializer<Color> = Color.serializer()
+ println(colorSerializer.descriptor)
+}
diff --git a/guide/example/example-serializer-03.kt b/guide/example/example-serializer-03.kt
new file mode 100644
index 00000000..cbf8772e
--- /dev/null
+++ b/guide/example/example-serializer-03.kt
@@ -0,0 +1,17 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer03
+
+import kotlinx.serialization.*
+
+@Serializable
+@SerialName("Color")
+class Color(val rgb: Int)
+
+@Serializable
+@SerialName("Box")
+class Box<T>(val contents: T)
+
+fun main() {
+ val boxedColorSerializer = Box.serializer(Color.serializer())
+ println(boxedColorSerializer.descriptor)
+}
diff --git a/guide/example/example-serializer-04.kt b/guide/example/example-serializer-04.kt
new file mode 100644
index 00000000..455cc1eb
--- /dev/null
+++ b/guide/example/example-serializer-04.kt
@@ -0,0 +1,10 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer04
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+
+fun main() {
+ val intSerializer: KSerializer<Int> = Int.serializer()
+ println(intSerializer.descriptor)
+}
diff --git a/guide/example/example-serializer-05.kt b/guide/example/example-serializer-05.kt
new file mode 100644
index 00000000..b9583c89
--- /dev/null
+++ b/guide/example/example-serializer-05.kt
@@ -0,0 +1,10 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer05
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+
+fun main() {
+ val stringListSerializer: KSerializer<List<String>> = ListSerializer(String.serializer())
+ println(stringListSerializer.descriptor)
+}
diff --git a/guide/example/example-serializer-06.kt b/guide/example/example-serializer-06.kt
new file mode 100644
index 00000000..0c8be2e1
--- /dev/null
+++ b/guide/example/example-serializer-06.kt
@@ -0,0 +1,13 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer06
+
+import kotlinx.serialization.*
+
+@Serializable
+@SerialName("Color")
+class Color(val rgb: Int)
+
+fun main() {
+ val stringToColorMapSerializer: KSerializer<Map<String, Color>> = serializer()
+ println(stringToColorMapSerializer.descriptor)
+}
diff --git a/guide/example/example-serializer-07.kt b/guide/example/example-serializer-07.kt
new file mode 100644
index 00000000..3ebafa21
--- /dev/null
+++ b/guide/example/example-serializer-07.kt
@@ -0,0 +1,29 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer07
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+object ColorAsStringSerializer : KSerializer<Color> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: Color) {
+ val string = value.rgb.toString(16).padStart(6, '0')
+ encoder.encodeString(string)
+ }
+
+ override fun deserialize(decoder: Decoder): Color {
+ val string = decoder.decodeString()
+ return Color(string.toInt(16))
+ }
+}
+
+@Serializable(with = ColorAsStringSerializer::class)
+class Color(val rgb: Int)
+
+fun main() {
+ val green = Color(0x00ff00)
+ println(Json.encodeToString(green))
+}
diff --git a/guide/example/example-serializer-08.kt b/guide/example/example-serializer-08.kt
new file mode 100644
index 00000000..73c8c810
--- /dev/null
+++ b/guide/example/example-serializer-08.kt
@@ -0,0 +1,29 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer08
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+object ColorAsStringSerializer : KSerializer<Color> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: Color) {
+ val string = value.rgb.toString(16).padStart(6, '0')
+ encoder.encodeString(string)
+ }
+
+ override fun deserialize(decoder: Decoder): Color {
+ val string = decoder.decodeString()
+ return Color(string.toInt(16))
+ }
+}
+
+@Serializable(with = ColorAsStringSerializer::class)
+class Color(val rgb: Int)
+
+fun main() {
+ val color = Json.decodeFromString<Color>("\"00ff00\"")
+ println(color.rgb) // prints 65280
+}
diff --git a/guide/example/example-serializer-09.kt b/guide/example/example-serializer-09.kt
new file mode 100644
index 00000000..4fe71119
--- /dev/null
+++ b/guide/example/example-serializer-09.kt
@@ -0,0 +1,34 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer09
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+object ColorAsStringSerializer : KSerializer<Color> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: Color) {
+ val string = value.rgb.toString(16).padStart(6, '0')
+ encoder.encodeString(string)
+ }
+
+ override fun deserialize(decoder: Decoder): Color {
+ val string = decoder.decodeString()
+ return Color(string.toInt(16))
+ }
+}
+
+@Serializable(with = ColorAsStringSerializer::class)
+data class Color(val rgb: Int)
+
+@Serializable
+data class Settings(val background: Color, val foreground: Color)
+
+fun main() {
+ val data = Settings(Color(0xffffff), Color(0))
+ val string = Json.encodeToString(data)
+ println(string)
+ require(Json.decodeFromString<Settings>(string) == data)
+}
diff --git a/guide/example/example-serializer-10.kt b/guide/example/example-serializer-10.kt
new file mode 100644
index 00000000..2b756524
--- /dev/null
+++ b/guide/example/example-serializer-10.kt
@@ -0,0 +1,36 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer10
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+import kotlinx.serialization.builtins.IntArraySerializer
+
+class ColorIntArraySerializer : KSerializer<Color> {
+ private val delegateSerializer = IntArraySerializer()
+ override val descriptor = SerialDescriptor("Color", delegateSerializer.descriptor)
+
+ override fun serialize(encoder: Encoder, value: Color) {
+ val data = intArrayOf(
+ (value.rgb shr 16) and 0xFF,
+ (value.rgb shr 8) and 0xFF,
+ value.rgb and 0xFF
+ )
+ encoder.encodeSerializableValue(delegateSerializer, data)
+ }
+
+ override fun deserialize(decoder: Decoder): Color {
+ val array = decoder.decodeSerializableValue(delegateSerializer)
+ return Color((array[0] shl 16) or (array[1] shl 8) or array[2])
+ }
+}
+
+@Serializable(with = ColorIntArraySerializer::class)
+class Color(val rgb: Int)
+
+fun main() {
+ val green = Color(0x00ff00)
+ println(Json.encodeToString(green))
+}
diff --git a/guide/example/example-serializer-11.kt b/guide/example/example-serializer-11.kt
new file mode 100644
index 00000000..3931aa02
--- /dev/null
+++ b/guide/example/example-serializer-11.kt
@@ -0,0 +1,36 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer11
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+@Serializable
+@SerialName("Color")
+private class ColorSurrogate(val r: Int, val g: Int, val b: Int) {
+ init {
+ require(r in 0..255 && g in 0..255 && b in 0..255)
+ }
+}
+
+object ColorSerializer : KSerializer<Color> {
+ override val descriptor: SerialDescriptor = ColorSurrogate.serializer().descriptor
+
+ override fun serialize(encoder: Encoder, value: Color) {
+ val surrogate = ColorSurrogate((value.rgb shr 16) and 0xff, (value.rgb shr 8) and 0xff, value.rgb and 0xff)
+ encoder.encodeSerializableValue(ColorSurrogate.serializer(), surrogate)
+ }
+
+ override fun deserialize(decoder: Decoder): Color {
+ val surrogate = decoder.decodeSerializableValue(ColorSurrogate.serializer())
+ return Color((surrogate.r shl 16) or (surrogate.g shl 8) or surrogate.b)
+ }
+}
+
+@Serializable(with = ColorSerializer::class)
+class Color(val rgb: Int)
+fun main() {
+ val green = Color(0x00ff00)
+ println(Json.encodeToString(green))
+}
diff --git a/guide/example/example-serializer-12.kt b/guide/example/example-serializer-12.kt
new file mode 100644
index 00000000..e53c7032
--- /dev/null
+++ b/guide/example/example-serializer-12.kt
@@ -0,0 +1,52 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer12
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+object ColorAsObjectSerializer : KSerializer<Color> {
+
+ override val descriptor: SerialDescriptor =
+ buildClassSerialDescriptor("Color") {
+ element<Int>("r")
+ element<Int>("g")
+ element<Int>("b")
+ }
+
+ override fun serialize(encoder: Encoder, value: Color) =
+ encoder.encodeStructure(descriptor) {
+ encodeIntElement(descriptor, 0, (value.rgb shr 16) and 0xff)
+ encodeIntElement(descriptor, 1, (value.rgb shr 8) and 0xff)
+ encodeIntElement(descriptor, 2, value.rgb and 0xff)
+ }
+
+ override fun deserialize(decoder: Decoder): Color =
+ decoder.decodeStructure(descriptor) {
+ var r = -1
+ var g = -1
+ var b = -1
+ while (true) {
+ when (val index = decodeElementIndex(descriptor)) {
+ 0 -> r = decodeIntElement(descriptor, 0)
+ 1 -> g = decodeIntElement(descriptor, 1)
+ 2 -> b = decodeIntElement(descriptor, 2)
+ CompositeDecoder.DECODE_DONE -> break
+ else -> error("Unexpected index: $index")
+ }
+ }
+ require(r in 0..255 && g in 0..255 && b in 0..255)
+ Color((r shl 16) or (g shl 8) or b)
+ }
+}
+
+@Serializable(with = ColorAsObjectSerializer::class)
+data class Color(val rgb: Int)
+
+fun main() {
+ val color = Color(0x00ff00)
+ val string = Json.encodeToString(color)
+ println(string)
+ require(Json.decodeFromString<Color>(string) == color)
+}
diff --git a/guide/example/example-serializer-13.kt b/guide/example/example-serializer-13.kt
new file mode 100644
index 00000000..f2b08882
--- /dev/null
+++ b/guide/example/example-serializer-13.kt
@@ -0,0 +1,56 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer13
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+object ColorAsObjectSerializer : KSerializer<Color> {
+
+ override val descriptor: SerialDescriptor =
+ buildClassSerialDescriptor("Color") {
+ element<Int>("r")
+ element<Int>("g")
+ element<Int>("b")
+ }
+
+ override fun serialize(encoder: Encoder, value: Color) =
+ encoder.encodeStructure(descriptor) {
+ encodeIntElement(descriptor, 0, (value.rgb shr 16) and 0xff)
+ encodeIntElement(descriptor, 1, (value.rgb shr 8) and 0xff)
+ encodeIntElement(descriptor, 2, value.rgb and 0xff)
+ }
+
+ override fun deserialize(decoder: Decoder): Color =
+ decoder.decodeStructure(descriptor) {
+ var r = -1
+ var g = -1
+ var b = -1
+ if (decodeSequentially()) { // sequential decoding protocol
+ r = decodeIntElement(descriptor, 0)
+ g = decodeIntElement(descriptor, 1)
+ b = decodeIntElement(descriptor, 2)
+ } else while (true) {
+ when (val index = decodeElementIndex(descriptor)) {
+ 0 -> r = decodeIntElement(descriptor, 0)
+ 1 -> g = decodeIntElement(descriptor, 1)
+ 2 -> b = decodeIntElement(descriptor, 2)
+ CompositeDecoder.DECODE_DONE -> break
+ else -> error("Unexpected index: $index")
+ }
+ }
+ require(r in 0..255 && g in 0..255 && b in 0..255)
+ Color((r shl 16) or (g shl 8) or b)
+ }
+}
+
+@Serializable(with = ColorAsObjectSerializer::class)
+data class Color(val rgb: Int)
+
+fun main() {
+ val color = Color(0x00ff00)
+ val string = Json.encodeToString(color)
+ println(string)
+ require(Json.decodeFromString<Color>(string) == color)
+}
diff --git a/guide/example/example-serializer-14.kt b/guide/example/example-serializer-14.kt
new file mode 100644
index 00000000..684290d7
--- /dev/null
+++ b/guide/example/example-serializer-14.kt
@@ -0,0 +1,21 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer14
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+import java.util.Date
+import java.text.SimpleDateFormat
+
+object DateAsLongSerializer : KSerializer<Date> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
+ override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
+ override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
+}
+
+fun main() {
+ val kotlin10ReleaseDate = SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00")
+ println(Json.encodeToString(DateAsLongSerializer, kotlin10ReleaseDate))
+}
diff --git a/guide/example/example-serializer-15.kt b/guide/example/example-serializer-15.kt
new file mode 100644
index 00000000..a508846f
--- /dev/null
+++ b/guide/example/example-serializer-15.kt
@@ -0,0 +1,28 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer15
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+import java.util.Date
+import java.text.SimpleDateFormat
+
+object DateAsLongSerializer : KSerializer<Date> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
+ override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
+ override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
+}
+
+@Serializable
+class ProgrammingLanguage(
+ val name: String,
+ @Serializable(with = DateAsLongSerializer::class)
+ val stableReleaseDate: Date
+)
+
+fun main() {
+ val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-serializer-16.kt b/guide/example/example-serializer-16.kt
new file mode 100644
index 00000000..157208fd
--- /dev/null
+++ b/guide/example/example-serializer-16.kt
@@ -0,0 +1,25 @@
+@file:UseSerializers(DateAsLongSerializer::class)
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer16
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+import java.util.Date
+import java.text.SimpleDateFormat
+
+object DateAsLongSerializer : KSerializer<Date> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
+ override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
+ override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
+}
+
+@Serializable
+class ProgrammingLanguage(val name: String, val stableReleaseDate: Date)
+
+fun main() {
+ val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-serializer-17.kt b/guide/example/example-serializer-17.kt
new file mode 100644
index 00000000..e6c488e1
--- /dev/null
+++ b/guide/example/example-serializer-17.kt
@@ -0,0 +1,26 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer17
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+@Serializable(with = BoxSerializer::class)
+data class Box<T>(val contents: T)
+
+class BoxSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Box<T>> {
+ override val descriptor: SerialDescriptor = dataSerializer.descriptor
+ override fun serialize(encoder: Encoder, value: Box<T>) = dataSerializer.serialize(encoder, value.contents)
+ override fun deserialize(decoder: Decoder) = Box(dataSerializer.deserialize(decoder))
+}
+
+@Serializable
+data class Project(val name: String)
+
+fun main() {
+ val box = Box(Project("kotlinx.serialization"))
+ val string = Json.encodeToString(box)
+ println(string)
+ println(Json.decodeFromString<Box<Project>>(string))
+}
diff --git a/guide/example/example-serializer-18.kt b/guide/example/example-serializer-18.kt
new file mode 100644
index 00000000..91626316
--- /dev/null
+++ b/guide/example/example-serializer-18.kt
@@ -0,0 +1,22 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer18
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+import java.util.Date
+import java.text.SimpleDateFormat
+
+@Serializable
+class ProgrammingLanguage(
+ val name: String,
+ @Contextual
+ val stableReleaseDate: Date
+)
+
+fun main() {
+ val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
+ println(Json.encodeToString(data))
+}
diff --git a/guide/example/example-serializer-19.kt b/guide/example/example-serializer-19.kt
new file mode 100644
index 00000000..da51db3e
--- /dev/null
+++ b/guide/example/example-serializer-19.kt
@@ -0,0 +1,35 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer19
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+import kotlinx.serialization.modules.*
+import java.util.Date
+import java.text.SimpleDateFormat
+
+object DateAsLongSerializer : KSerializer<Date> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
+ override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
+ override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
+}
+
+@Serializable
+class ProgrammingLanguage(
+ val name: String,
+ @Contextual
+ val stableReleaseDate: Date
+)
+
+private val module = SerializersModule {
+ contextual(DateAsLongSerializer)
+}
+
+val format = Json { serializersModule = module }
+
+fun main() {
+ val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
+ println(format.encodeToString(data))
+}
diff --git a/guide/example/example-serializer-20.kt b/guide/example/example-serializer-20.kt
new file mode 100644
index 00000000..7b4e71c9
--- /dev/null
+++ b/guide/example/example-serializer-20.kt
@@ -0,0 +1,18 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer20
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+// NOT @Serializable
+class Project(val name: String, val language: String)
+
+@Serializer(forClass = Project::class)
+object ProjectSerializer
+
+fun main() {
+ val data = Project("kotlinx.serialization", "Kotlin")
+ println(Json.encodeToString(ProjectSerializer, data))
+}
diff --git a/guide/example/example-serializer-21.kt b/guide/example/example-serializer-21.kt
new file mode 100644
index 00000000..95879074
--- /dev/null
+++ b/guide/example/example-serializer-21.kt
@@ -0,0 +1,28 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer21
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+// NOT @Serializable, will use external serializer
+class Project(
+ // val in a primary constructor -- serialized
+ val name: String
+) {
+ var stars: Int = 0 // property with getter & setter -- serialized
+
+ val path: String // getter only -- not serialized
+ get() = "kotlin/$name"
+
+ private var locked: Boolean = false // private, not accessible -- not serialized
+}
+
+@Serializer(forClass = Project::class)
+object ProjectSerializer
+
+fun main() {
+ val data = Project("kotlinx.serialization").apply { stars = 9000 }
+ println(Json.encodeToString(ProjectSerializer, data))
+}
diff --git a/guide/test/BasicSerializationTest.kt b/guide/test/BasicSerializationTest.kt
new file mode 100644
index 00000000..74c4433d
--- /dev/null
+++ b/guide/test/BasicSerializationTest.kt
@@ -0,0 +1,144 @@
+// This file was automatically generated from basic-serialization.md by Knit tool. Do not edit.
+package example.test
+
+import org.junit.Test
+import kotlinx.knit.test.*
+
+class BasicSerializationTest {
+ @Test
+ fun testExampleBasic01() {
+ captureOutput("ExampleBasic01") { example.exampleBasic01.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" kotlinx.serialization.SerializationException: Serializer for class 'Project' is not found.",
+ "Mark the class as @Serializable or provide the serializer explicitly."
+ )
+ }
+
+ @Test
+ fun testExampleBasic02() {
+ captureOutput("ExampleBasic02") { example.exampleBasic02.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}"
+ )
+ }
+
+ @Test
+ fun testExampleBasic03() {
+ captureOutput("ExampleBasic03") { example.exampleBasic03.main() }.verifyOutputLines(
+ "Project(name=kotlinx.serialization, language=Kotlin)"
+ )
+ }
+
+ @Test
+ fun testExampleClasses01() {
+ captureOutput("ExampleClasses01") { example.exampleClasses01.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"stars\":9000}"
+ )
+ }
+
+ @Test
+ fun testExampleClasses02() {
+ captureOutput("ExampleClasses02") { example.exampleClasses02.main() }.verifyOutputLines(
+ "{\"owner\":\"kotlin\",\"name\":\"kotlinx.serialization\"}"
+ )
+ }
+
+ @Test
+ fun testExampleClasses03() {
+ captureOutput("ExampleClasses03") { example.exampleClasses03.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" java.lang.IllegalArgumentException: name cannot be empty"
+ )
+ }
+
+ @Test
+ fun testExampleClasses04() {
+ captureOutput("ExampleClasses04") { example.exampleClasses04.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" kotlinx.serialization.MissingFieldException: Field 'language' is required for type with serial name 'example.exampleClasses04.Project', but it was missing at path: $"
+ )
+ }
+
+ @Test
+ fun testExampleClasses05() {
+ captureOutput("ExampleClasses05") { example.exampleClasses05.main() }.verifyOutputLines(
+ "Project(name=kotlinx.serialization, language=Kotlin)"
+ )
+ }
+
+ @Test
+ fun testExampleClasses06() {
+ captureOutput("ExampleClasses06") { example.exampleClasses06.main() }.verifyOutputLines(
+ "Project(name=kotlinx.serialization, language=Kotlin)"
+ )
+ }
+
+ @Test
+ fun testExampleClasses07() {
+ captureOutput("ExampleClasses07") { example.exampleClasses07.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" kotlinx.serialization.MissingFieldException: Field 'language' is required for type with serial name 'example.exampleClasses07.Project', but it was missing at path: $"
+ )
+ }
+
+ @Test
+ fun testExampleClasses08() {
+ captureOutput("ExampleClasses08") { example.exampleClasses08.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 42: Encountered an unknown key 'language' at path: $.name",
+ "Use 'ignoreUnknownKeys = true' in 'Json {}' builder to ignore unknown keys."
+ )
+ }
+
+ @Test
+ fun testExampleClasses09() {
+ captureOutput("ExampleClasses09") { example.exampleClasses09.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\"}"
+ )
+ }
+
+ @Test
+ fun testExampleClasses10() {
+ captureOutput("ExampleClasses10") { example.exampleClasses10.main() }.verifyOutputLines(
+ "{\"name\":\"Alice\",\"projects\":[{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}]}",
+ "{\"name\":\"Bob\"}"
+ )
+ }
+
+ @Test
+ fun testExampleClasses11() {
+ captureOutput("ExampleClasses11") { example.exampleClasses11.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\"}"
+ )
+ }
+
+ @Test
+ fun testExampleClasses12() {
+ captureOutput("ExampleClasses12") { example.exampleClasses12.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 52: Expected string literal but 'null' literal was found at path: $.language",
+ "Use 'coerceInputValues = true' in 'Json {}` builder to coerce nulls to default values."
+ )
+ }
+
+ @Test
+ fun testExampleClasses13() {
+ captureOutput("ExampleClasses13") { example.exampleClasses13.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"owner\":{\"name\":\"kotlin\"}}"
+ )
+ }
+
+ @Test
+ fun testExampleClasses14() {
+ captureOutput("ExampleClasses14") { example.exampleClasses14.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"owner\":{\"name\":\"kotlin\"},\"maintainer\":{\"name\":\"kotlin\"}}"
+ )
+ }
+
+ @Test
+ fun testExampleClasses15() {
+ captureOutput("ExampleClasses15") { example.exampleClasses15.main() }.verifyOutputLines(
+ "{\"a\":{\"contents\":42},\"b\":{\"contents\":{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}}}"
+ )
+ }
+
+ @Test
+ fun testExampleClasses16() {
+ captureOutput("ExampleClasses16") { example.exampleClasses16.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"lang\":\"Kotlin\"}"
+ )
+ }
+}
diff --git a/guide/test/BuiltinClassesTest.kt b/guide/test/BuiltinClassesTest.kt
new file mode 100644
index 00000000..28273328
--- /dev/null
+++ b/guide/test/BuiltinClassesTest.kt
@@ -0,0 +1,85 @@
+// This file was automatically generated from builtin-classes.md by Knit tool. Do not edit.
+package example.test
+
+import org.junit.Test
+import kotlinx.knit.test.*
+
+class BuiltinClassesTest {
+ @Test
+ fun testExampleBuiltin01() {
+ captureOutput("ExampleBuiltin01") { example.exampleBuiltin01.main() }.verifyOutputLines(
+ "{\"answer\":42,\"pi\":3.141592653589793}"
+ )
+ }
+
+ @Test
+ fun testExampleBuiltin02() {
+ captureOutput("ExampleBuiltin02") { example.exampleBuiltin02.main() }.verifyOutputLines(
+ "{\"signature\":2067120338512882656}"
+ )
+ }
+
+ @Test
+ fun testExampleBuiltin03() {
+ captureOutput("ExampleBuiltin03") { example.exampleBuiltin03.main() }.verifyOutputLines(
+ "{\"signature\":\"2067120338512882656\"}"
+ )
+ }
+
+ @Test
+ fun testExampleBuiltin04() {
+ captureOutput("ExampleBuiltin04") { example.exampleBuiltin04.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"status\":\"SUPPORTED\"}"
+ )
+ }
+
+ @Test
+ fun testExampleBuiltin05() {
+ captureOutput("ExampleBuiltin05") { example.exampleBuiltin05.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"status\":\"maintained\"}"
+ )
+ }
+
+ @Test
+ fun testExampleBuiltin06() {
+ captureOutput("ExampleBuiltin06") { example.exampleBuiltin06.main() }.verifyOutputLines(
+ "{\"first\":1,\"second\":{\"name\":\"kotlinx.serialization\"}}"
+ )
+ }
+
+ @Test
+ fun testExampleBuiltin07() {
+ captureOutput("ExampleBuiltin07") { example.exampleBuiltin07.main() }.verifyOutputLines(
+ "[{\"name\":\"kotlinx.serialization\"},{\"name\":\"kotlinx.coroutines\"}]"
+ )
+ }
+
+ @Test
+ fun testExampleBuiltin08() {
+ captureOutput("ExampleBuiltin08") { example.exampleBuiltin08.main() }.verifyOutputLines(
+ "[{\"name\":\"kotlinx.serialization\"},{\"name\":\"kotlinx.coroutines\"}]"
+ )
+ }
+
+ @Test
+ fun testExampleBuiltin09() {
+ captureOutput("ExampleBuiltin09") { example.exampleBuiltin09.main() }.verifyOutputLines(
+ "Data(a=[42, 42], b=[42])"
+ )
+ }
+
+ @Test
+ fun testExampleBuiltin10() {
+ captureOutput("ExampleBuiltin10") { example.exampleBuiltin10.main() }.verifyOutputLines(
+ "{\"1\":{\"name\":\"kotlinx.serialization\"},\"2\":{\"name\":\"kotlinx.coroutines\"}}"
+ )
+ }
+
+ @Test
+ fun testExampleBuiltin11() {
+ captureOutput("ExampleBuiltin11") { example.exampleBuiltin11.main() }.verifyOutputLines(
+ "{}",
+ "{}"
+ )
+ }
+}
diff --git a/guide/test/FormatsTest.kt b/guide/test/FormatsTest.kt
new file mode 100644
index 00000000..c20a9f2a
--- /dev/null
+++ b/guide/test/FormatsTest.kt
@@ -0,0 +1,141 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.test
+
+import org.junit.Test
+import kotlinx.knit.test.*
+
+class FormatsTest {
+ @Test
+ fun testExampleFormats01() {
+ captureOutput("ExampleFormats01") { example.exampleFormats01.main() }.verifyOutputLines(
+ "{BF}dnameukotlinx.serializationhlanguagefKotlin{FF}",
+ "Project(name=kotlinx.serialization, language=Kotlin)"
+ )
+ }
+
+ @Test
+ fun testExampleFormats02() {
+ captureOutput("ExampleFormats02") { example.exampleFormats02.main() }.verifyOutputLines(
+ "Project(name=kotlinx.serialization)"
+ )
+ }
+
+ @Test
+ fun testExampleFormats03() {
+ captureOutput("ExampleFormats03") { example.exampleFormats03.main() }.verifyOutputLines(
+ "{BF}etype2D{01}{02}{03}{04}etype4{9F}{05}{06}{07}{08}{FF}{FF}",
+ "Data(type2=[1, 2, 3, 4], type4=[5, 6, 7, 8])"
+ )
+ }
+
+ @Test
+ fun testExampleFormats04() {
+ captureOutput("ExampleFormats04") { example.exampleFormats04.main() }.verifyOutputLines(
+ "{0A}{15}kotlinx.serialization{12}{06}Kotlin",
+ "Project(name=kotlinx.serialization, language=Kotlin)"
+ )
+ }
+
+ @Test
+ fun testExampleFormats05() {
+ captureOutput("ExampleFormats05") { example.exampleFormats05.main() }.verifyOutputLines(
+ "{0A}{15}kotlinx.serialization{1A}{06}Kotlin",
+ "Project(name=kotlinx.serialization, language=Kotlin)"
+ )
+ }
+
+ @Test
+ fun testExampleFormats06() {
+ captureOutput("ExampleFormats06") { example.exampleFormats06.main() }.verifyOutputLines(
+ "{08}{01}{10}{03}{1D}{03}{00}{00}{00}"
+ )
+ }
+
+ @Test
+ fun testExampleFormats07() {
+ captureOutput("ExampleFormats07") { example.exampleFormats07.main() }.verifyOutputLines(
+ "{08}{01}{08}{02}{08}{03}",
+ "Data(a=[1, 2, 3], b=[])"
+ )
+ }
+
+ @Test
+ fun testExampleFormats08() {
+ captureOutput("ExampleFormats08") { example.exampleFormats08.main() }.verifyOutputLines(
+ "syntax = \"proto2\";",
+ "",
+ "",
+ "// serial name 'example.exampleFormats08.SampleData'",
+ "message SampleData {",
+ " required int64 amount = 1;",
+ " optional string description = 2;",
+ " // WARNING: a default value decoded when value is missing",
+ " optional string department = 3;",
+ "}",
+ ""
+ )
+ }
+
+ @Test
+ fun testExampleFormats09() {
+ captureOutput("ExampleFormats09") { example.exampleFormats09.main() }.verifyOutputLines(
+ "name = kotlinx.serialization",
+ "owner.name = kotlin"
+ )
+ }
+
+ @Test
+ fun testExampleFormats10() {
+ captureOutput("ExampleFormats10") { example.exampleFormats10.main() }.verifyOutputLines(
+ "[kotlinx.serialization, kotlin, 9000]"
+ )
+ }
+
+ @Test
+ fun testExampleFormats11() {
+ captureOutput("ExampleFormats11") { example.exampleFormats11.main() }.verifyOutputLines(
+ "[kotlinx.serialization, kotlin, 9000]",
+ "Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=9000)"
+ )
+ }
+
+ @Test
+ fun testExampleFormats12() {
+ captureOutput("ExampleFormats12") { example.exampleFormats12.main() }.verifyOutputLines(
+ "[kotlinx.serialization, kotlin, 9000]",
+ "Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=9000)"
+ )
+ }
+
+ @Test
+ fun testExampleFormats13() {
+ captureOutput("ExampleFormats13") { example.exampleFormats13.main() }.verifyOutputLines(
+ "[kotlinx.serialization, 2, kotlin, jetbrains, 9000]",
+ "Project(name=kotlinx.serialization, owners=[User(name=kotlin), User(name=jetbrains)], votes=9000)"
+ )
+ }
+
+ @Test
+ fun testExampleFormats14() {
+ captureOutput("ExampleFormats14") { example.exampleFormats14.main() }.verifyOutputLines(
+ "[kotlinx.serialization, !!, kotlin, NULL]",
+ "Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=null)"
+ )
+ }
+
+ @Test
+ fun testExampleFormats15() {
+ captureOutput("ExampleFormats15") { example.exampleFormats15.main() }.verifyOutputLines(
+ "{00}{15}kotlinx.serialization{00}{06}Kotlin",
+ "Project(name=kotlinx.serialization, language=Kotlin)"
+ )
+ }
+
+ @Test
+ fun testExampleFormats16() {
+ captureOutput("ExampleFormats16") { example.exampleFormats16.main() }.verifyOutputLines(
+ "{00}{15}kotlinx.serialization{04}{0A}{0B}{0C}{0D}",
+ "Project(name=kotlinx.serialization, attachment=[10, 11, 12, 13])"
+ )
+ }
+}
diff --git a/guide/test/JsonTest.kt b/guide/test/JsonTest.kt
new file mode 100644
index 00000000..c92f57bf
--- /dev/null
+++ b/guide/test/JsonTest.kt
@@ -0,0 +1,163 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.test
+
+import org.junit.Test
+import kotlinx.knit.test.*
+
+class JsonTest {
+ @Test
+ fun testExampleJson01() {
+ captureOutput("ExampleJson01") { example.exampleJson01.main() }.verifyOutputLines(
+ "{",
+ " \"name\": \"kotlinx.serialization\",",
+ " \"language\": \"Kotlin\"",
+ "}"
+ )
+ }
+
+ @Test
+ fun testExampleJson02() {
+ captureOutput("ExampleJson02") { example.exampleJson02.main() }.verifyOutputLines(
+ "Project(name=kotlinx.serialization, status=SUPPORTED, votes=9000)"
+ )
+ }
+
+ @Test
+ fun testExampleJson03() {
+ captureOutput("ExampleJson03") { example.exampleJson03.main() }.verifyOutputLines(
+ "Project(name=kotlinx.serialization)"
+ )
+ }
+
+ @Test
+ fun testExampleJson04() {
+ captureOutput("ExampleJson04") { example.exampleJson04.main() }.verifyOutputLines(
+ "Project(name=kotlinx.serialization)",
+ "Project(name=kotlinx.coroutines)"
+ )
+ }
+
+ @Test
+ fun testExampleJson05() {
+ captureOutput("ExampleJson05") { example.exampleJson05.main() }.verifyOutputLines(
+ "Project(name=kotlinx.serialization, language=Kotlin)"
+ )
+ }
+
+ @Test
+ fun testExampleJson06() {
+ captureOutput("ExampleJson06") { example.exampleJson06.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\",\"website\":null}"
+ )
+ }
+
+ @Test
+ fun testExampleJson07() {
+ captureOutput("ExampleJson07") { example.exampleJson07.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}",
+ "Project(name=kotlinx.serialization, language=Kotlin, version=1.2.2, website=null, description=null)"
+ )
+ }
+
+ @Test
+ fun testExampleJson08() {
+ captureOutput("ExampleJson08") { example.exampleJson08.main() }.verifyOutputLines(
+ "[{\"name\":\"kotlinx.serialization\"},\"Serialization\",{\"name\":\"kotlinx.coroutines\"},\"Coroutines\"]"
+ )
+ }
+
+ @Test
+ fun testExampleJson09() {
+ captureOutput("ExampleJson09") { example.exampleJson09.main() }.verifyOutputLines(
+ "{\"value\":NaN}"
+ )
+ }
+
+ @Test
+ fun testExampleJson10() {
+ captureOutput("ExampleJson10") { example.exampleJson10.main() }.verifyOutputLines(
+ "{\"#class\":\"owned\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
+ )
+ }
+
+ @Test
+ fun testExampleJson11() {
+ captureOutput("ExampleJson11") { example.exampleJson11.main() }.verifyOutputLines(
+ "{\"message\":{\"message_type\":\"my.app.BaseMessage\",\"message\":\"not found\"},\"error\":{\"message_type\":\"my.app.GenericError\",\"error_code\":404}}"
+ )
+ }
+
+ @Test
+ fun testExampleJson12() {
+ captureOutput("ExampleJson12") { example.exampleJson12.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}"
+ )
+ }
+
+ @Test
+ fun testExampleJson13() {
+ captureOutput("ExampleJson13") { example.exampleJson13.main() }.verifyOutputLines(
+ "9042"
+ )
+ }
+
+ @Test
+ fun testExampleJson14() {
+ captureOutput("ExampleJson14") { example.exampleJson14.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"owner\":{\"name\":\"kotlin\"},\"forks\":[{\"votes\":42},{\"votes\":9000}]}"
+ )
+ }
+
+ @Test
+ fun testExampleJson15() {
+ captureOutput("ExampleJson15") { example.exampleJson15.main() }.verifyOutputLines(
+ "Project(name=kotlinx.serialization, language=Kotlin)"
+ )
+ }
+
+ @Test
+ fun testExampleJson16() {
+ captureOutput("ExampleJson16") { example.exampleJson16.main() }.verifyOutputLines(
+ "Project(name=kotlinx.serialization, users=[User(name=kotlin)])",
+ "Project(name=kotlinx.serialization, users=[User(name=kotlin), User(name=jetbrains)])"
+ )
+ }
+
+ @Test
+ fun testExampleJson17() {
+ captureOutput("ExampleJson17") { example.exampleJson17.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"users\":{\"name\":\"kotlin\"}}"
+ )
+ }
+
+ @Test
+ fun testExampleJson18() {
+ captureOutput("ExampleJson18") { example.exampleJson18.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}",
+ "{\"name\":\"kotlinx.serialization\"}"
+ )
+ }
+
+ @Test
+ fun testExampleJson19() {
+ captureOutput("ExampleJson19") { example.exampleJson19.main() }.verifyOutputLines(
+ "[{\"name\":\"kotlinx.serialization\",\"owner\":\"kotlin\"},{\"name\":\"example\"}]",
+ "[OwnedProject(name=kotlinx.serialization, owner=kotlin), BasicProject(name=example)]"
+ )
+ }
+
+ @Test
+ fun testExampleJson20() {
+ captureOutput("ExampleJson20") { example.exampleJson20.main() }.verifyOutputLines(
+ "[{\"name\":\"kotlinx.serialization\"},{\"error\":\"Not found\"}]",
+ "[Ok(data=Project(name=kotlinx.serialization)), Error(message=Not found)]"
+ )
+ }
+
+ @Test
+ fun testExampleJson21() {
+ captureOutput("ExampleJson21") { example.exampleJson21.main() }.verifyOutputLines(
+ "UnknownProject(name=example, details={\"type\":\"unknown\",\"maintainer\":\"Unknown\",\"license\":\"Apache 2.0\"})"
+ )
+ }
+}
diff --git a/guide/test/PolymorphismTest.kt b/guide/test/PolymorphismTest.kt
new file mode 100644
index 00000000..e82dd689
--- /dev/null
+++ b/guide/test/PolymorphismTest.kt
@@ -0,0 +1,152 @@
+// This file was automatically generated from polymorphism.md by Knit tool. Do not edit.
+package example.test
+
+import org.junit.Test
+import kotlinx.knit.test.*
+
+class PolymorphismTest {
+ @Test
+ fun testExamplePoly01() {
+ captureOutput("ExamplePoly01") { example.examplePoly01.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.coroutines\"}"
+ )
+ }
+
+ @Test
+ fun testExamplePoly02() {
+ captureOutput("ExamplePoly02") { example.examplePoly02.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" kotlinx.serialization.SerializationException: Serializer for class 'OwnedProject' is not found.",
+ "Mark the class as @Serializable or provide the serializer explicitly."
+ )
+ }
+
+ @Test
+ fun testExamplePoly03() {
+ captureOutput("ExamplePoly03") { example.examplePoly03.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" kotlinx.serialization.SerializationException: Class 'OwnedProject' is not registered for polymorphic serialization in the scope of 'Project'.",
+ "Mark the base class as 'sealed' or register the serializer explicitly."
+ )
+ }
+
+ @Test
+ fun testExamplePoly04() {
+ captureOutput("ExamplePoly04") { example.examplePoly04.main() }.verifyOutputLines(
+ "{\"type\":\"example.examplePoly04.OwnedProject\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
+ )
+ }
+
+ @Test
+ fun testExamplePoly05() {
+ captureOutput("ExamplePoly05") { example.examplePoly05.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
+ )
+ }
+
+ @Test
+ fun testExamplePoly06() {
+ captureOutput("ExamplePoly06") { example.examplePoly06.main() }.verifyOutputLines(
+ "{\"type\":\"owned\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
+ )
+ }
+
+ @Test
+ fun testExamplePoly07() {
+ captureOutput("ExamplePoly07") { example.examplePoly07.main() }.verifyOutputLines(
+ "{\"type\":\"owned\",\"status\":\"open\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
+ )
+ }
+
+ @Test
+ fun testExamplePoly08() {
+ captureOutput("ExamplePoly08") { example.examplePoly08.main() }.verifyOutputLines(
+ "[{\"type\":\"example.examplePoly08.EmptyResponse\"},{\"type\":\"example.examplePoly08.TextResponse\",\"text\":\"OK\"}]"
+ )
+ }
+
+ @Test
+ fun testExamplePoly09() {
+ captureOutput("ExamplePoly09") { example.examplePoly09.main() }.verifyOutputLines(
+ "{\"type\":\"owned\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
+ )
+ }
+
+ @Test
+ fun testExamplePoly10() {
+ captureOutput("ExamplePoly10") { example.examplePoly10.main() }.verifyOutputLinesStart(
+ "{\"type\":\"owned\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
+ )
+ }
+
+ @Test
+ fun testExamplePoly11() {
+ captureOutput("ExamplePoly11") { example.examplePoly11.main() }.verifyOutputLines(
+ "{\"project\":{\"type\":\"owned\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}}"
+ )
+ }
+
+ @Test
+ fun testExamplePoly12() {
+ captureOutput("ExamplePoly12") { example.examplePoly12.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" kotlinx.serialization.SerializationException: Serializer for class 'Any' is not found.",
+ "Mark the class as @Serializable or provide the serializer explicitly."
+ )
+ }
+
+ @Test
+ fun testExamplePoly13() {
+ captureOutput("ExamplePoly13") { example.examplePoly13.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" kotlinx.serialization.SerializationException: Serializer for class 'Any' is not found.",
+ "Mark the class as @Serializable or provide the serializer explicitly."
+ )
+ }
+
+ @Test
+ fun testExamplePoly14() {
+ captureOutput("ExamplePoly14") { example.examplePoly14.main() }.verifyOutputLines(
+ "{\"type\":\"owned\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
+ )
+ }
+
+ @Test
+ fun testExamplePoly15() {
+ captureOutput("ExamplePoly15") { example.examplePoly15.main() }.verifyOutputLines(
+ "{\"project\":{\"type\":\"owned\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}}"
+ )
+ }
+
+ @Test
+ fun testExamplePoly16() {
+ captureOutput("ExamplePoly16") { example.examplePoly16.main() }.verifyOutputLines(
+ "{\"project\":{\"type\":\"owned\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"},\"any\":{\"type\":\"owned\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}}"
+ )
+ }
+
+ @Test
+ fun testExamplePoly17() {
+ captureOutput("ExamplePoly17") { example.examplePoly17.main() }.verifyOutputLines(
+ "{\"type\":\"OkResponse\",\"data\":{\"type\":\"OwnedProject\",\"name\":\"kotlinx.serialization\",\"owner\":\"kotlin\"}}",
+ "OkResponse(data=OwnedProject(name=kotlinx.serialization, owner=kotlin))"
+ )
+ }
+
+ @Test
+ fun testExamplePoly18() {
+ captureOutput("ExamplePoly18") { example.examplePoly18.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" kotlinx.serialization.json.internal.JsonDecodingException: Polymorphic serializer was not found for class discriminator 'unknown'"
+ )
+ }
+
+ @Test
+ fun testExamplePoly19() {
+ captureOutput("ExamplePoly19") { example.examplePoly19.main() }.verifyOutputLines(
+ "[BasicProject(name=example, type=unknown), OwnedProject(name=kotlinx.serialization, owner=kotlin)]"
+ )
+ }
+
+ @Test
+ fun testExamplePoly20() {
+ captureOutput("ExamplePoly20") { example.examplePoly20.main() }.verifyOutputLines(
+ "{\"type\":\"Cat\",\"catType\":\"Tabby\"}"
+ )
+ }
+}
diff --git a/guide/test/ReadmeTest.kt b/guide/test/ReadmeTest.kt
new file mode 100644
index 00000000..27fa65b8
--- /dev/null
+++ b/guide/test/ReadmeTest.kt
@@ -0,0 +1,15 @@
+// This file was automatically generated from README.md by Knit tool. Do not edit.
+package example.test
+
+import org.junit.Test
+import kotlinx.knit.test.*
+
+class ReadmeTest {
+ @Test
+ fun testExampleReadme01() {
+ captureOutput("ExampleReadme01") { example.exampleReadme01.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}",
+ "Project(name=kotlinx.serialization, language=Kotlin)"
+ )
+ }
+}
diff --git a/guide/test/SerializersTest.kt b/guide/test/SerializersTest.kt
new file mode 100644
index 00000000..0871b829
--- /dev/null
+++ b/guide/test/SerializersTest.kt
@@ -0,0 +1,156 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.test
+
+import org.junit.Test
+import kotlinx.knit.test.*
+
+class SerializersTest {
+ @Test
+ fun testExampleSerializer01() {
+ captureOutput("ExampleSerializer01") { example.exampleSerializer01.main() }.verifyOutputLines(
+ "{\"rgb\":65280}"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer02() {
+ captureOutput("ExampleSerializer02") { example.exampleSerializer02.main() }.verifyOutputLines(
+ "Color(rgb: kotlin.Int)"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer03() {
+ captureOutput("ExampleSerializer03") { example.exampleSerializer03.main() }.verifyOutputLines(
+ "Box(contents: Color)"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer04() {
+ captureOutput("ExampleSerializer04") { example.exampleSerializer04.main() }.verifyOutputLines(
+ "PrimitiveDescriptor(kotlin.Int)"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer05() {
+ captureOutput("ExampleSerializer05") { example.exampleSerializer05.main() }.verifyOutputLines(
+ "kotlin.collections.ArrayList(PrimitiveDescriptor(kotlin.String))"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer06() {
+ captureOutput("ExampleSerializer06") { example.exampleSerializer06.main() }.verifyOutputLines(
+ "kotlin.collections.LinkedHashMap(PrimitiveDescriptor(kotlin.String), Color(rgb: kotlin.Int))"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer07() {
+ captureOutput("ExampleSerializer07") { example.exampleSerializer07.main() }.verifyOutputLines(
+ "\"00ff00\""
+ )
+ }
+
+ @Test
+ fun testExampleSerializer08() {
+ captureOutput("ExampleSerializer08") { example.exampleSerializer08.main() }.verifyOutputLines(
+ "65280"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer09() {
+ captureOutput("ExampleSerializer09") { example.exampleSerializer09.main() }.verifyOutputLines(
+ "{\"background\":\"ffffff\",\"foreground\":\"000000\"}"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer10() {
+ captureOutput("ExampleSerializer10") { example.exampleSerializer10.main() }.verifyOutputLines(
+ "[0,255,0]"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer11() {
+ captureOutput("ExampleSerializer11") { example.exampleSerializer11.main() }.verifyOutputLines(
+ "{\"r\":0,\"g\":255,\"b\":0}"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer12() {
+ captureOutput("ExampleSerializer12") { example.exampleSerializer12.main() }.verifyOutputLines(
+ "{\"r\":0,\"g\":255,\"b\":0}"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer13() {
+ captureOutput("ExampleSerializer13") { example.exampleSerializer13.main() }.verifyOutputLines(
+ "{\"r\":0,\"g\":255,\"b\":0}"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer14() {
+ captureOutput("ExampleSerializer14") { example.exampleSerializer14.main() }.verifyOutputLines(
+ "1455494400000"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer15() {
+ captureOutput("ExampleSerializer15") { example.exampleSerializer15.main() }.verifyOutputLines(
+ "{\"name\":\"Kotlin\",\"stableReleaseDate\":1455494400000}"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer16() {
+ captureOutput("ExampleSerializer16") { example.exampleSerializer16.main() }.verifyOutputLines(
+ "{\"name\":\"Kotlin\",\"stableReleaseDate\":1455494400000}"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer17() {
+ captureOutput("ExampleSerializer17") { example.exampleSerializer17.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\"}",
+ "Box(contents=Project(name=kotlinx.serialization))"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer18() {
+ captureOutput("ExampleSerializer18") { example.exampleSerializer18.main() }.verifyOutputLinesStart(
+ "Exception in thread \"main\" kotlinx.serialization.SerializationException: Serializer for class 'Date' is not found.",
+ "Mark the class as @Serializable or provide the serializer explicitly."
+ )
+ }
+
+ @Test
+ fun testExampleSerializer19() {
+ captureOutput("ExampleSerializer19") { example.exampleSerializer19.main() }.verifyOutputLines(
+ "{\"name\":\"Kotlin\",\"stableReleaseDate\":1455494400000}"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer20() {
+ captureOutput("ExampleSerializer20") { example.exampleSerializer20.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}"
+ )
+ }
+
+ @Test
+ fun testExampleSerializer21() {
+ captureOutput("ExampleSerializer21") { example.exampleSerializer21.main() }.verifyOutputLines(
+ "{\"name\":\"kotlinx.serialization\",\"stars\":9000}"
+ )
+ }
+}
diff --git a/integration-test/build.gradle b/integration-test/build.gradle
new file mode 100644
index 00000000..911cee18
--- /dev/null
+++ b/integration-test/build.gradle
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+buildscript {
+ ext.serialization_version = mainLibVersion
+
+ repositories {
+ mavenLocal()
+ mavenCentral()
+ maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
+ }
+}
+
+// see ../settings.gradle so this plugins could be resolved
+plugins {
+ id 'kotlin-multiplatform'
+ id 'kotlinx-serialization'
+ id 'org.jetbrains.kotlin.kapt'
+}
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+ maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
+}
+
+group 'com.example'
+version '0.0.1'
+
+apply plugin: 'maven-publish'
+
+kotlin {
+ // Switching module kind for JS is required to run tests
+ js {
+ nodejs {}
+ configure([compilations.main, compilations.test]) {
+ kotlinOptions {
+ sourceMap = true
+ moduleKind = "umd"
+ metaInfo = true
+ }
+ }
+ }
+ jvm {
+ withJava()
+ }
+ // For ARM, should be changed to iosArm32 or iosArm64
+ // For Linux, should be changed to e.g. linuxX64
+ // For MacOS, should be changed to e.g. macosX64
+ // For Windows, should be changed to e.g. mingwX64
+ macosX64("macos")
+ linuxX64("linux")
+ sourceSets {
+ commonMain {
+ dependencies {
+ implementation kotlin('stdlib-common')
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$serialization_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serialization_version"
+ }
+ }
+ commonTest {
+ dependencies {
+ implementation kotlin('test-common')
+ implementation kotlin('test-annotations-common')
+ }
+ }
+ jvmMain {
+ dependencies {
+ implementation kotlin('stdlib-jdk8')
+ implementation 'com.google.dagger:dagger:2.13'
+ }
+ }
+ jvmTest {
+ dependencies {
+ implementation kotlin('test')
+ implementation kotlin('test-junit')
+ }
+ }
+ jsMain {
+ dependencies {
+ implementation kotlin('stdlib-js')
+
+ }
+ }
+ jsTest {
+ dependencies {
+ implementation kotlin('test-js')
+ }
+ }
+ macosMain {
+ dependencies {
+ }
+ }
+ macosTest {}
+ linuxMain {
+ kotlin.srcDirs = ["src/macosMain/kotlin"]
+ }
+ linuxTest {
+ kotlin.srcDirs = ["src/macosTest/kotlin"]
+ }
+ }
+ sourceSets.all {
+ languageSettings {
+ useExperimentalAnnotation('kotlin.Experimental') // annotation FQ-name
+ }
+ }
+}
+
+dependencies {
+ kapt 'com.google.dagger:dagger-compiler:2.13'
+}
+
+task run dependsOn "check"
diff --git a/integration-test/gradle.properties b/integration-test/gradle.properties
new file mode 100644
index 00000000..398917a4
--- /dev/null
+++ b/integration-test/gradle.properties
@@ -0,0 +1,18 @@
+#
+# Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+#
+
+mainKotlinVersion=1.6.21
+mainLibVersion=1.3.4-SNAPSHOT
+
+kotlin.code.style=official
+kotlin.js.compiler=both
+
+gradle_node_version = 1.2.0
+node_version = 8.9.3
+npm_version = 5.7.1
+mocha_version = 4.1.0
+mocha_teamcity_reporter_version = 2.2.2
+source_map_support_version = 0.5.3
+
+#org.jetbrains.kotlin.native.jvmArgs=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5007
diff --git a/integration-test/gradle/wrapper/gradle-wrapper.jar b/integration-test/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..7454180f
--- /dev/null
+++ b/integration-test/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/integration-test/gradle/wrapper/gradle-wrapper.properties b/integration-test/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..669386b8
--- /dev/null
+++ b/integration-test/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/integration-test/gradlew b/integration-test/gradlew
new file mode 100755
index 00000000..1b6c7873
--- /dev/null
+++ b/integration-test/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/integration-test/gradlew.bat b/integration-test/gradlew.bat
new file mode 100644
index 00000000..ac1b06f9
--- /dev/null
+++ b/integration-test/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/integration-test/settings.gradle b/integration-test/settings.gradle
new file mode 100644
index 00000000..2360ae47
--- /dev/null
+++ b/integration-test/settings.gradle
@@ -0,0 +1,24 @@
+pluginManagement {
+ resolutionStrategy {
+ eachPlugin {
+ if (requested.id.id == "kotlin-multiplatform") {
+ useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:$mainKotlinVersion")
+ }
+ if (requested.id.id == "org.jetbrains.kotlin.kapt") {
+ useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:$mainKotlinVersion")
+ }
+ if (requested.id.id == "kotlinx-serialization") {
+ useModule("org.jetbrains.kotlin:kotlin-serialization:$mainKotlinVersion")
+ }
+ }
+ }
+
+ repositories {
+ mavenCentral()
+ maven { url 'https://plugins.gradle.org/m2/' }
+ maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
+ mavenLocal()
+ }
+}
+
+rootProject.name = 'kotlinx-serialization-integration-test'
diff --git a/integration-test/src/commonMain/kotlin/sample/Data.kt b/integration-test/src/commonMain/kotlin/sample/Data.kt
new file mode 100644
index 00000000..fc780b4c
--- /dev/null
+++ b/integration-test/src/commonMain/kotlin/sample/Data.kt
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package sample
+
+import kotlinx.serialization.Polymorphic
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.Transient
+import kotlinx.serialization.modules.SerializersModule
+import kotlinx.serialization.modules.polymorphic
+import kotlinx.serialization.modules.subclass
+import kotlinx.serialization.protobuf.ProtoNumber
+
+@Serializable
+data class IntData(val intV: Int)
+
+@Serializable
+data class Box<T>(val boxed: T)
+
+@Serializable
+data class HasBox<T>(val desc: String, val boxes: List<Box<T>>)
+
+@Serializable
+data class Data(
+ val s: String,
+ val box: Box<Int> = Box(42),
+ val boxes: HasBox<String> = HasBox("boxes", listOf(Box("foo"), Box("bar"))),
+ val m: Map<Int, String> = emptyMap()
+)
+
+// this has implicit @Polymorphic
+interface IMessage {
+ val body: String
+}
+
+// and this class too has implicit @Polymorphic
+@Serializable
+abstract class Message() : IMessage {
+ abstract override val body: String
+}
+
+@Polymorphic
+@Serializable
+@SerialName("SimpleMessage") // to cut out package prefix
+open class SimpleMessage() : Message() {
+ override var body: String = "Simple"
+}
+
+@Serializable
+@SerialName("DoubleSimpleMessage")
+class DoubleSimpleMessage(val body2: String) : SimpleMessage()
+
+@Serializable
+@SerialName("MessageWithId")
+open class MessageWithId(val id: Int, override val body: String) : Message()
+
+@Serializable
+class Holder(
+ val iMessage: IMessage,
+ val iMessageList: List<IMessage>,
+ val message: Message,
+ val msgSet: Set<Message>,
+ val simple: SimpleMessage,
+ // all above should be polymorphic
+ val withId: MessageWithId // but this not
+)
+
+@Serializable
+class GenericMessage<T : IMessage, V: Any>(
+ @Polymorphic val value: T,
+ @Polymorphic val value2: V
+)
+
+@Serializable
+abstract class AbstractSerializable {
+ public abstract val rootState: String // no backing field
+
+ val publicState: String = "A"
+}
+
+@Serializable
+open class SerializableBase: AbstractSerializable() {
+
+
+ private val privateState: String = "B" // still should be serialized
+
+ @Transient
+ private val privateTransientState = "C" // not serialized: explicitly transient
+
+ val notAState: String // not serialized: no backing field
+ get() = "D"
+
+ override val rootState: String
+ get() = "E" // still not serializable
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is SerializableBase) return false
+
+ if (privateState != other.privateState) return false
+ if (privateTransientState != other.privateTransientState) return false
+
+ return true
+ }
+}
+
+@Serializable
+class Derived(val derivedState: Int): SerializableBase() {
+ override val rootState: String = "foo" // serializable!
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is Derived) return false
+ if (!super.equals(other)) return false
+
+ if (derivedState != other.derivedState) return false
+ if (rootState != other.rootState) return false
+
+ return true
+ }
+}
+
+@Serializable
+open class Base1(open var state1: String) {
+ override fun toString(): String {
+ return "Base1(state1='$state1')"
+ }
+}
+
+@Serializable
+class Derived2(@SerialName("state2") override var state1: String): Base1(state1) {
+ override fun toString(): String {
+ return "Derived2(state1='$state1')"
+ }
+}
+
+@Serializable
+open class PolyBase(@ProtoNumber(1) val id: Int) {
+ override fun hashCode(): Int {
+ return id
+ }
+
+ override fun toString(): String {
+ return "PolyBase(id=$id)"
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as PolyBase
+
+ if (id != other.id) return false
+
+ return true
+ }
+
+}
+
+@Serializable
+data class PolyDerived(@ProtoNumber(2) val s: String) : PolyBase(1)
+
+val BaseAndDerivedModule = SerializersModule {
+ polymorphic(PolyBase::class, PolyBase.serializer()) {
+ subclass(PolyDerived.serializer())
+ }
+}
+
+@Serializable
+data class MyPolyData(val data: Map<String, @Polymorphic Any>)
+
+@Serializable
+data class MyPolyDataWithPolyBase(
+ val data: Map<String, @Polymorphic Any>,
+ @Polymorphic val polyBase: PolyBase
+)
+
+enum class Attitude { POSITIVE, NEUTRAL, NEGATIVE }
+
+@Serializable
+data class Tree(val name: String, val left: Tree? = null, val right: Tree? = null)
+
+@Serializable
+data class Zoo(
+ val unit: Unit,
+ val boolean: Boolean,
+ val byte: Byte,
+ val short: Short,
+ val int: Int,
+ val long: Long,
+ val float: Float,
+ val double: Double,
+ val char: Char,
+ val string: String,
+ val enum: Attitude,
+ val intData: IntData,
+ val unitN: Unit?,
+ val booleanN: Boolean?,
+ val byteN: Byte?,
+ val shortN: Short?,
+ val intN: Int?,
+ val longN: Long?,
+ val floatN: Float?,
+ val doubleN: Double?,
+ val charN: Char?,
+ val stringN: String?,
+ val enumN: Attitude?,
+ val intDataN: IntData?,
+ val listInt: List<Int>,
+ val listIntN: List<Int?>,
+ val listNInt: Set<Int>?,
+ val listNIntN: MutableSet<Int?>?,
+ val listListEnumN: List<List<Attitude?>>,
+ val listIntData: List<IntData>,
+ val listIntDataN: MutableList<IntData?>,
+ val tree: Tree,
+ val mapStringInt: Map<String,Int>,
+ val mapIntStringN: Map<Int,String?>,
+ val arrays: ZooWithArrays
+)
+
+@Serializable
+data class ZooWithArrays(
+ val arrByte: Array<Byte>,
+ val arrInt: Array<Int>,
+ val arrIntN: Array<Int?>,
+ val arrIntData: Array<IntData>
+
+) {
+ override fun equals(other: Any?) = other is ZooWithArrays &&
+ arrByte.contentEquals(other.arrByte) &&
+ arrInt.contentEquals(other.arrInt) &&
+ arrIntN.contentEquals(other.arrIntN) &&
+ arrIntData.contentEquals(other.arrIntData)
+}
+
+val zoo = Zoo(
+ Unit, true, 10, 20, 30, 40, 50f, 60.0, 'A', "Str0", Attitude.POSITIVE, IntData(70),
+ null, null, 11, 21, 31, 41, 51f, 61.0, 'B', "Str1", Attitude.NEUTRAL, null,
+ listOf(1, 2, 3),
+ listOf(4, 5, null),
+ setOf(6, 7, 8),
+ mutableSetOf(null, 9, 10),
+ listOf(listOf(Attitude.NEGATIVE, null)),
+ listOf(IntData(1), IntData(2), IntData(3)),
+ mutableListOf(IntData(1), null, IntData(3)),
+ Tree("root", Tree("left"), Tree("right", Tree("right.left"), Tree("right.right"))),
+ mapOf("one" to 1, "two" to 2, "three" to 3),
+ mapOf(0 to null, 1 to "first", 2 to "second"),
+ ZooWithArrays(
+ arrayOf(1, 2, 3),
+ arrayOf(100, 200, 300),
+ arrayOf(null, -1, -2),
+ arrayOf(IntData(1), IntData(2))
+ )
+)
diff --git a/integration-test/src/commonMain/kotlin/sample/GeoCoordinate.kt b/integration-test/src/commonMain/kotlin/sample/GeoCoordinate.kt
new file mode 100644
index 00000000..a8a0e514
--- /dev/null
+++ b/integration-test/src/commonMain/kotlin/sample/GeoCoordinate.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package sample
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class GeoCoordinate(
+ val latitude: Double = 0.0,
+ val longitude: Double = 0.0
+) {
+
+ init {
+ require(latitude >= 0) { "latitude must be non-negative" }
+ require(longitude >= 0) { "longitude must be non-negative" }
+ }
+}
+
diff --git a/integration-test/src/commonMain/kotlin/sample/MultiFileHierarchyModuleA.kt b/integration-test/src/commonMain/kotlin/sample/MultiFileHierarchyModuleA.kt
new file mode 100644
index 00000000..3afcd51b
--- /dev/null
+++ b/integration-test/src/commonMain/kotlin/sample/MultiFileHierarchyModuleA.kt
@@ -0,0 +1,30 @@
+package sample
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+
+@Serializable
+abstract class EmptyBase
+
+@Serializable
+class EmptyClassA : EmptyBase()
+
+@Serializable
+open class Vehicle {
+ var name: String? = null
+ var color: String? = null
+}
+
+@Serializable
+abstract class Snippet(
+ @SerialName("objectFieldName") val objectFieldName: String,
+ @SerialName("aaa") val aaa: String
+)
+
+@Serializable
+abstract class NotInConstructorBase {
+ // b should precede a for testing
+ val b = "val b"
+ val a = "val a"
+}
diff --git a/integration-test/src/commonMain/kotlin/sample/Sample.kt b/integration-test/src/commonMain/kotlin/sample/Sample.kt
new file mode 100644
index 00000000..bc9adaa2
--- /dev/null
+++ b/integration-test/src/commonMain/kotlin/sample/Sample.kt
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package sample
+
+expect object Platform {
+ val name: String
+}
+
+fun hello(): String = "Hello from ${Platform.name}"
diff --git a/integration-test/src/commonTest/kotlin/sample/BasicTypesSerializationTest.kt b/integration-test/src/commonTest/kotlin/sample/BasicTypesSerializationTest.kt
new file mode 100644
index 00000000..206348bb
--- /dev/null
+++ b/integration-test/src/commonTest/kotlin/sample/BasicTypesSerializationTest.kt
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package sample
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.AbstractDecoder
+import kotlinx.serialization.encoding.AbstractEncoder
+import kotlinx.serialization.encoding.CompositeDecoder
+import kotlinx.serialization.encoding.CompositeEncoder
+import kotlinx.serialization.modules.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNotSame
+
+class BasicTypesSerializationTest {
+
+ enum class Attitude { POSITIVE, NEUTRAL, NEGATIVE }
+
+ @Serializable
+ data class Tree(val name: String, val left: Tree? = null, val right: Tree? = null)
+
+ @Serializable
+ data class TypesUmbrella(
+ val unit: Unit,
+ val boolean: Boolean,
+ val byte: Byte,
+ val short: Short,
+ val int: Int,
+ val long: Long,
+ val float: Float,
+ val double: Double,
+ val char: Char,
+ val string: String,
+ val enum: Attitude,
+ val intData: IntData,
+ val unitN: Unit?,
+ val booleanN: Boolean?,
+ val byteN: Byte?,
+ val shortN: Short?,
+ val intN: Int?,
+ val longN: Long?,
+ val floatN: Float?,
+ val doubleN: Double?,
+ val charN: Char?,
+ val stringN: String?,
+ val enumN: Attitude?,
+ val intDataN: IntData?,
+ val listInt: List<Int>,
+ val listIntN: List<Int?>,
+ val listNInt: Set<Int>?,
+ val listNIntN: MutableSet<Int?>?,
+ val listListEnumN: List<List<Attitude?>>,
+ val listIntData: List<IntData>,
+ val listIntDataN: MutableList<IntData?>,
+ val tree: Tree,
+ val mapStringInt: Map<String, Int>,
+ val mapIntStringN: Map<Int, String?>,
+ val arrays: ArraysUmbrella
+ )
+
+ @Serializable
+ data class ArraysUmbrella(
+ val arrByte: Array<Byte>,
+ val arrInt: Array<Int>,
+ val arrIntN: Array<Int?>,
+ val arrIntData: Array<IntData>
+ ) {
+ override fun equals(other: Any?) = other is ArraysUmbrella &&
+ arrByte.contentEquals(other.arrByte) &&
+ arrInt.contentEquals(other.arrInt) &&
+ arrIntN.contentEquals(other.arrIntN) &&
+ arrIntData.contentEquals(other.arrIntData)
+ }
+
+ val data = TypesUmbrella(
+ Unit, true, 10, 20, 30, 40, 50f, 60.0, 'A', "Str0", Attitude.POSITIVE, IntData(70),
+ null, null, 11, 21, 31, 41, 51f, 61.0, 'B', "Str1", Attitude.NEUTRAL, null,
+ listOf(1, 2, 3),
+ listOf(4, 5, null),
+ setOf(6, 7, 8),
+ mutableSetOf(null, 9, 10),
+ listOf(listOf(Attitude.NEGATIVE, null)),
+ listOf(IntData(1), IntData(2), IntData(3)),
+ mutableListOf(IntData(1), null, IntData(3)),
+ Tree("root", Tree("left"), Tree("right", Tree("right.left"), Tree("right.right"))),
+ mapOf("one" to 1, "two" to 2, "three" to 3),
+ mapOf(0 to null, 1 to "first", 2 to "second"),
+ ArraysUmbrella(
+ arrayOf(1, 2, 3),
+ arrayOf(100, 200, 300),
+ arrayOf(null, -1, -2),
+ arrayOf(IntData(1), IntData(2))
+ )
+ )
+
+ @Serializable
+ class WithSecondaryConstructor(var someProperty: Int) {
+ var test: String = "Test"
+
+ constructor() : this(42)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is WithSecondaryConstructor) return false
+
+ if (someProperty != other.someProperty) return false
+ if (test != other.test) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = someProperty
+ result = 31 * result + test.hashCode()
+ return result
+ }
+ }
+
+
+ // KeyValue Input/Output
+
+ @OptIn(ExperimentalSerializationApi::class)
+ class KeyValueOutput(val sb: StringBuilder) : AbstractEncoder() {
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+ sb.append('{')
+ return this
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ sb.append('}')
+ }
+
+ override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
+ if (index > 0) sb.append(", ")
+ sb.append(descriptor.getElementName(index));
+ sb.append(':')
+ return true
+ }
+
+ override fun encodeNull() {
+ sb.append("null")
+ }
+
+ override fun encodeValue(value: Any) {
+ sb.append(value)
+ }
+
+ override fun encodeString(value: String) {
+ sb.append('"')
+ sb.append(value)
+ sb.append('"')
+ }
+
+ override fun encodeChar(value: Char) = encodeString(value.toString())
+ }
+
+ class KeyValueInput(val inp: Parser) : AbstractDecoder() {
+
+ override val serializersModule: SerializersModule = EmptySerializersModule
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+ inp.expectAfterWhiteSpace('{')
+ return this
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) = inp.expectAfterWhiteSpace('}')
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ inp.skipWhitespace(',')
+ val name = inp.nextUntil(':', '}')
+ if (name.isEmpty())
+ return CompositeDecoder.DECODE_DONE
+ val index = descriptor.getElementIndex(name)
+ inp.expect(':')
+ return index
+ }
+
+ private fun readToken(): String {
+ inp.skipWhitespace()
+ return inp.nextUntil(' ', ',', '}')
+ }
+
+ override fun decodeNotNullMark(): Boolean {
+ inp.skipWhitespace()
+ if (inp.cur != 'n'.toInt()) return true
+ return false
+ }
+
+ override fun decodeNull(): Nothing? {
+ check(readToken() == "null") { "'null' expected" }
+ return null
+ }
+
+ override fun decodeBoolean(): Boolean = readToken() == "true"
+ override fun decodeByte(): Byte = readToken().toByte()
+ override fun decodeShort(): Short = readToken().toShort()
+ override fun decodeInt(): Int = readToken().toInt()
+ override fun decodeLong(): Long = readToken().toLong()
+ override fun decodeFloat(): Float = readToken().toFloat()
+ override fun decodeDouble(): Double = readToken().toDouble()
+
+ override fun decodeEnum(enumDescriptor: SerialDescriptor): Int {
+ return readToken().toInt()
+ }
+
+ override fun decodeString(): String {
+ inp.expectAfterWhiteSpace('"')
+ val value = inp.nextUntil('"')
+ inp.expect('"')
+ return value
+ }
+
+ override fun decodeChar(): Char = decodeString().single()
+ }
+
+ // Very simple char-by-char parser
+ class Parser(private val inp: StringReader) {
+ var cur: Int = inp.read()
+
+ fun next() {
+ cur = inp.read()
+ }
+
+ fun skipWhitespace(vararg c: Char) {
+ while (cur >= 0 && (cur.toChar().isWhitespace() || cur.toChar() in c))
+ next()
+ }
+
+ fun expect(c: Char) {
+ check(cur == c.toInt()) { "Expected '$c'" }
+ next()
+ }
+
+ fun expectAfterWhiteSpace(c: Char) {
+ skipWhitespace()
+ expect(c)
+ }
+
+ fun nextUntil(vararg c: Char): String {
+ val sb = StringBuilder()
+ while (cur >= 0 && cur.toChar() !in c) {
+ sb.append(cur.toChar())
+ next()
+ }
+ return sb.toString()
+ }
+ }
+
+
+ class StringReader(val str: String) {
+ private var position: Int = 0
+ fun read(): Int = when (position) {
+ str.length -> -1
+ else -> str[position++].toInt()
+ }
+ }
+
+ @Test
+ fun testKvSerialization() {
+ // serialize to string
+ val sb = StringBuilder()
+ val out = KeyValueOutput(sb)
+ out.encodeSerializableValue(TypesUmbrella.serializer(), data)
+ // deserialize from string
+ val str = sb.toString()
+ val inp = KeyValueInput(Parser(StringReader(str)))
+ val other = inp.decodeSerializableValue(TypesUmbrella.serializer())
+ // assert we've got it back from string
+ assertEquals(data, other)
+ assertNotSame(data, other)
+ }
+
+ @Test
+ fun someConstructor() {
+ assertStringFormAndRestored("""{"someProperty":42,"test":"Test"}""", WithSecondaryConstructor(), WithSecondaryConstructor.serializer())
+ }
+}
diff --git a/integration-test/src/commonTest/kotlin/sample/JsonTest.kt b/integration-test/src/commonTest/kotlin/sample/JsonTest.kt
new file mode 100644
index 00000000..6b704354
--- /dev/null
+++ b/integration-test/src/commonTest/kotlin/sample/JsonTest.kt
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:Suppress("UNCHECKED_CAST")
+
+package sample
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.reflect.*
+import kotlin.test.*
+
+public val jsonWithDefaults = Json { encodeDefaults = true }
+
+class JsonTest {
+
+ private val originalData = Data("Hello")
+ private val originalString =
+ """{"s":"Hello","box":{"boxed":42},"boxes":{"desc":"boxes","boxes":[{"boxed":"foo"},{"boxed":"bar"}]},"m":{}}"""
+ private val nonstrict: Json = Json {
+ isLenient = true
+ ignoreUnknownKeys = true
+ allowSpecialFloatingPointValues = true
+ useArrayPolymorphism = true
+ encodeDefaults = true
+ }
+
+ @Test
+ fun testStringForm() {
+ val str = jsonWithDefaults.encodeToString(Data.serializer(), originalData)
+ assertEquals(originalString, str)
+ }
+
+ @Test
+ fun testSerializeBack() {
+ val restored = jsonWithDefaults.decodeFromString(Data.serializer(), originalString)
+ assertEquals(originalData, restored)
+ }
+
+ private fun genTestData(): Holder {
+ var cnt = -1
+ fun gen(): MessageWithId {
+ cnt++
+ return MessageWithId(cnt, "Message #$cnt")
+ }
+
+ return Holder(gen(), listOf(gen(), gen()), gen(), setOf(SimpleMessage()), DoubleSimpleMessage("DoubleSimple"), gen())
+ }
+
+ private val testModule = SerializersModule {
+ listOf(Message::class, IMessage::class, SimpleMessage::class).forEach { clz ->
+ polymorphic(clz as KClass<Any>) {
+ subclass(SimpleMessage::class)
+ subclass(DoubleSimpleMessage::class)
+ subclass(MessageWithId::class)
+ }
+ }
+ }
+
+ @Test
+ fun testEnablesImplicitlyOnInterfacesAndAbstractClasses() {
+ val json = Json(jsonWithDefaults) { useArrayPolymorphism = true; prettyPrint = false; serializersModule = testModule }
+ val data = genTestData()
+ assertEquals("""{"iMessage":["MessageWithId",{"id":0,"body":"Message #0"}],"iMessageList":[["MessageWithId",{"id":1,"body":"Message #1"}],""" +
+ """["MessageWithId",{"id":2,"body":"Message #2"}]],"message":["MessageWithId",{"id":3,"body":"Message #3"}],"msgSet":[["SimpleMessage",""" +
+ """{"body":"Simple"}]],"simple":["DoubleSimpleMessage",{"body":"Simple","body2":"DoubleSimple"}],"withId":{"id":4,"body":"Message #4"}}""",
+ json.encodeToString(Holder.serializer(), data)
+ )
+ }
+
+ @Test
+ fun testPolymorphicForGenericUpperBound() {
+ if (Platform.name == "JS") return // Does not work with JS IR, see #1072
+ val generic = GenericMessage<Message, Any>(MessageWithId(42, "body"), "body2")
+ val serial = GenericMessage.serializer(Message.serializer(), Int.serializer() as KSerializer<Any>)
+ val json = Json {
+ useArrayPolymorphism = true
+ prettyPrint = false
+ serializersModule = testModule + SerializersModule {
+ polymorphic(Any::class) {
+ subclass(Int::class)
+ subclass(String::class)
+ }
+ }
+ }
+ val s = json.encodeToString(serial, generic)
+ assertEquals("""{"value":["MessageWithId",{"id":42,"body":"body"}],"value2":["kotlin.String","body2"]}""", s)
+ }
+
+ @Test
+ @OptIn(ExperimentalSerializationApi::class)
+ fun testDescriptor() {
+ val desc = Holder.serializer().descriptor
+ assertEquals(PolymorphicSerializer(IMessage::class).descriptor, desc.getElementDescriptor(0))
+ }
+
+ @Test
+ fun canBeSerializedAsDerived() {
+ val derived = Derived(42)
+ val msg = jsonWithDefaults.encodeToString(Derived.serializer(), derived)
+ assertEquals("""{"publicState":"A","privateState":"B","derivedState":42,"rootState":"foo"}""", msg)
+ val d2 = jsonWithDefaults.decodeFromString(Derived.serializer(), msg)
+ assertEquals(derived, d2)
+ }
+
+ @Test
+ fun canBeSerializedAsParent() {
+ val derived = Derived(42)
+ val msg = jsonWithDefaults.encodeToString(SerializableBase.serializer(), derived)
+ assertEquals("""{"publicState":"A","privateState":"B"}""", msg)
+ val d2 = jsonWithDefaults.decodeFromString(SerializableBase.serializer(), msg)
+ assertEquals(SerializableBase(), d2)
+ // no derivedState
+ assertFailsWith<SerializationException> { jsonWithDefaults.decodeFromString(Derived.serializer(), msg) }
+ }
+
+ @Test
+ fun testWithOpenProperty() {
+ val d = Derived2("foo")
+ val msgFull = Json.encodeToString(Derived2.serializer(), d)
+ assertEquals("""{"state1":"foo","state2":"foo"}""", msgFull)
+ assertEquals("""{"state1":"foo"}""", Json.encodeToString(Base1.serializer(), d))
+ val restored = Json.decodeFromString(Derived2.serializer(), msgFull)
+ val restored2 =
+ Json.decodeFromString(Derived2.serializer(), """{"state1":"bar","state2":"foo"}""") // state1 is ignored anyway
+ assertEquals("""Derived2(state1='foo')""", restored.toString())
+ assertEquals("""Derived2(state1='foo')""", restored2.toString())
+ }
+
+ @Suppress("NAME_SHADOWING")
+ private fun checkNotRegisteredMessage(exception: SerializationException) {
+ val expectedText =
+ "is not registered for polymorphic serialization in the scope of"
+ assertEquals(true, exception.message?.contains(expectedText))
+ }
+
+ @Test
+ fun failWithoutModulesWithCustomClass() {
+ checkNotRegisteredMessage(
+ assertFailsWith<SerializationException>("not registered") {
+ Json.encodeToString(
+ MyPolyData.serializer(),
+ MyPolyData(mapOf("a" to IntData(42)))
+ )
+ }
+ )
+ }
+
+ @Test
+ fun testWithModules() {
+ val json = Json {
+ useArrayPolymorphism = true; serializersModule = SerializersModule { polymorphic(Any::class) { subclass(IntData::class) } } }
+ assertStringFormAndRestored(
+ expected = """{"data":{"a":["sample.IntData",{"intV":42}]}}""",
+ original = MyPolyData(mapOf("a" to IntData(42))),
+ serializer = MyPolyData.serializer(),
+ format = json
+ )
+ }
+
+ /**
+ * This test should fail because PolyDerived registered in the scope of PolyBase, not kotlin.Any
+ */
+ @Test
+ fun failWithModulesNotInAnyScope() {
+ val json = Json { serializersModule = BaseAndDerivedModule }
+ checkNotRegisteredMessage(
+ assertFailsWith<SerializationException> {
+ json.encodeToString(
+ MyPolyData.serializer(),
+ MyPolyData(mapOf("a" to PolyDerived("foo")))
+ )
+ }
+ )
+ }
+
+ private val baseAndDerivedModuleAtAny = SerializersModule {
+ polymorphic(Any::class) {
+ subclass(PolyDerived::class)
+ }
+ }
+
+
+ @Test
+ fun testRebindModules() {
+ val json = Json { useArrayPolymorphism = true; serializersModule = baseAndDerivedModuleAtAny }
+ assertStringFormAndRestored(
+ expected = """{"data":{"a":["sample.PolyDerived",{"id":1,"s":"foo"}]}}""",
+ original = MyPolyData(mapOf("a" to PolyDerived("foo"))),
+ serializer = MyPolyData.serializer(),
+ format = json
+ )
+ }
+
+ /**
+ * This test should fail because PolyDerived registered in the scope of kotlin.Any, not PolyBase
+ */
+ @Test
+ fun failWithModulesNotInParticularScope() {
+ val json = Json { serializersModule = baseAndDerivedModuleAtAny }
+ checkNotRegisteredMessage(
+ assertFailsWith<SerializationException> {
+ json.encodeToString(
+ MyPolyDataWithPolyBase.serializer(),
+ MyPolyDataWithPolyBase(mapOf("a" to PolyDerived("foo")), PolyDerived("foo"))
+ )
+ }
+ )
+ }
+
+ @Test
+ fun testBindModules() {
+ val json = Json { useArrayPolymorphism = true; serializersModule = (baseAndDerivedModuleAtAny + BaseAndDerivedModule) }
+ assertStringFormAndRestored(
+ expected = """{"data":{"a":["sample.PolyDerived",{"id":1,"s":"foo"}]},"polyBase":["sample.PolyDerived",{"id":1,"s":"foo"}]}""",
+ original = MyPolyDataWithPolyBase(mapOf("a" to PolyDerived("foo")), PolyDerived("foo")),
+ serializer = MyPolyDataWithPolyBase.serializer(),
+ format = json
+ )
+ }
+
+ @Test
+ fun geoTest() {
+ val deser = nonstrict.decodeFromString(GeoCoordinate.serializer(), """{"latitude":1.0,"longitude":1.0}""")
+ assertEquals(GeoCoordinate(1.0, 1.0), deser)
+ }
+
+ @Test
+ fun geoTest2() {
+ val deser = nonstrict.decodeFromString(GeoCoordinate.serializer(), """{}""")
+ assertEquals(GeoCoordinate(0.0, 0.0), deser)
+ }
+
+ @Test
+ fun geoTestValidation() {
+ assertFailsWith<IllegalArgumentException> {
+ nonstrict.decodeFromString(GeoCoordinate.serializer(), """{"latitude":-1.0,"longitude":1.0}""")
+ }
+ }
+}
+
+inline fun <reified T : Any> assertStringFormAndRestored(
+ expected: String,
+ original: T,
+ serializer: KSerializer<T>,
+ format: Json = jsonWithDefaults,
+ printResult: Boolean = false
+) {
+ val string = format.encodeToString(serializer, original)
+ if (printResult) println("[Serialized form] $string")
+ assertEquals(expected, string)
+ val restored = format.decodeFromString(serializer, string)
+ if (printResult) println("[Restored form] $original")
+ assertEquals(original, restored)
+}
diff --git a/integration-test/src/commonTest/kotlin/sample/MultiFileHierarchyModuleB.kt b/integration-test/src/commonTest/kotlin/sample/MultiFileHierarchyModuleB.kt
new file mode 100644
index 00000000..01d8ca68
--- /dev/null
+++ b/integration-test/src/commonTest/kotlin/sample/MultiFileHierarchyModuleB.kt
@@ -0,0 +1,63 @@
+package sample
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+class EmptyClassB : EmptyBase()
+// TODO: Uncomment when https://youtrack.jetbrains.com/issue/KT-49865 is resolved
+//
+//@Serializable
+//open class Car : Vehicle() {
+// var maxSpeed: Int = 100
+//
+// override fun equals(other: Any?): Boolean {
+// if (this === other) return true
+// if (other !is Car) return false
+// if (name != other.name) return false
+// if (color != other.color) return false
+// if (maxSpeed != other.maxSpeed) return false
+//
+// return true
+// }
+//
+// override fun hashCode(): Int {
+// return maxSpeed.hashCode()
+// }
+//
+// override fun toString(): String {
+// return "Car(name=$name, color=$color, maxSpeed=$maxSpeed)"
+// }
+//}
+//
+//@Serializable
+//data class TestSnippet(
+// @SerialName("experiments") val experiments: List<String>
+//) : Snippet("test", "aaa")
+//
+//@Serializable
+//data class ScreenSnippet(
+// @SerialName("name") val name: String,
+// @SerialName("uuid") val uuid: String? = null,
+// @SerialName("source") val source: String? = null
+//) : Snippet("screen", "aaa")
+//
+//@Serializable
+//class NotInConstructorTest : NotInConstructorBase() {
+// val c = "val c"
+//
+// override fun equals(other: Any?): Boolean {
+// if (this === other) return true
+// if (other !is NotInConstructorTest) return false
+//
+// if (a != other.a) return false
+// if (b != other.b) return false
+// if (c != other.c) return false
+//
+// return true
+// }
+//
+// override fun hashCode(): Int {
+// return a.hashCode() * 31 + b.hashCode() * 31 + c.hashCode()
+// }
+//}
diff --git a/integration-test/src/commonTest/kotlin/sample/MultiFileHierarchyTest.kt b/integration-test/src/commonTest/kotlin/sample/MultiFileHierarchyTest.kt
new file mode 100644
index 00000000..74750ca0
--- /dev/null
+++ b/integration-test/src/commonTest/kotlin/sample/MultiFileHierarchyTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package sample
+
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.modules.SerializersModule
+import kotlinx.serialization.modules.polymorphic
+import kotlinx.serialization.modules.subclass
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class AbstractBaseTest {
+ @Test
+ fun concreteClassTest() {
+ val concrete = EmptyClassA()
+ val serialized: String = Json.encodeToString(EmptyClassA.serializer(), concrete)
+ // to ensure that parsed without exceptions
+ val parsed: EmptyClassA = Json.decodeFromString(EmptyClassA.serializer(), serialized)
+ }
+
+ @Test
+ fun stubConcreteClassTest() {
+ val concrete = EmptyClassB()
+ val serialized: String = Json.encodeToString(EmptyClassB.serializer(), concrete)
+ // to ensure that parsed without exceptions
+ val parsed: EmptyClassB = Json.decodeFromString(EmptyClassB.serializer(), serialized)
+ }
+
+// @Test
+// fun testCrossModuleInheritance() {
+// val json = Json { allowStructuredMapKeys = true; encodeDefaults = true }
+//
+// val car = Car()
+// car.maxSpeed = 100
+// car.name = "ford"
+// val s = json.encodeToString(Car.serializer(), car)
+// assertEquals("""{"name":"ford","color":null,"maxSpeed":100}""", s)
+// val restoredCar = json.decodeFromString(Car.serializer(), s)
+// assertEquals(car, restoredCar)
+// }
+//
+// @Test
+// fun testCrossModuleAbstractInheritance() {
+// val snippetModule = SerializersModule {
+// polymorphic(Snippet::class) {
+// subclass(ScreenSnippet.serializer())
+// subclass(TestSnippet.serializer())
+// }
+// }
+//
+// val json = Json {
+// serializersModule = snippetModule
+// encodeDefaults = true
+// }
+//
+// val testSnippet = TestSnippet(emptyList())
+// val screenSnippet = ScreenSnippet("one", "two", "three")
+// val s = json.encodeToString(TestSnippet.serializer(), testSnippet)
+// assertEquals(testSnippet, json.decodeFromString(TestSnippet.serializer(), s))
+// assertEquals("""{"objectFieldName":"test","aaa":"aaa","experiments":[]}""",
+// json.encodeToString(TestSnippet.serializer(), testSnippet)
+// )
+// assertStringFormAndRestored("""{"objectFieldName":"screen","aaa":"aaa","name":"one","uuid":"two","source":"three"}""",
+// screenSnippet,
+// ScreenSnippet.serializer(),
+// json
+// )
+// }
+//
+// @Test
+// fun testPropertiesNotInConstructor() {
+// assertStringFormAndRestored("""{"b":"val b","a":"val a","c":"val c"}""", NotInConstructorTest(), NotInConstructorTest.serializer())
+// }
+}
diff --git a/integration-test/src/jsMain/kotlin/sample/SampleJs.kt b/integration-test/src/jsMain/kotlin/sample/SampleJs.kt
new file mode 100644
index 00000000..bf38e006
--- /dev/null
+++ b/integration-test/src/jsMain/kotlin/sample/SampleJs.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample
+
+actual object Platform {
+ actual val name: String = "JS"
+}
diff --git a/integration-test/src/jsTest/kotlin/sample/SampleTestsJS.kt b/integration-test/src/jsTest/kotlin/sample/SampleTestsJS.kt
new file mode 100644
index 00000000..c16985c6
--- /dev/null
+++ b/integration-test/src/jsTest/kotlin/sample/SampleTestsJS.kt
@@ -0,0 +1,11 @@
+package sample
+
+import kotlin.test.Test
+import kotlin.test.assertTrue
+
+class SampleTestsJS {
+ @Test
+ fun testHello() {
+ assertTrue("JS" in hello())
+ }
+} \ No newline at end of file
diff --git a/integration-test/src/jvmMain/kotlin/sample/SampleJvm.kt b/integration-test/src/jvmMain/kotlin/sample/SampleJvm.kt
new file mode 100644
index 00000000..26852e06
--- /dev/null
+++ b/integration-test/src/jvmMain/kotlin/sample/SampleJvm.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample
+
+actual object Platform {
+ actual val name: String = "JVM"
+}
diff --git a/integration-test/src/jvmTest/kotlin/sample/SampleTestsJVM.kt b/integration-test/src/jvmTest/kotlin/sample/SampleTestsJVM.kt
new file mode 100644
index 00000000..04839915
--- /dev/null
+++ b/integration-test/src/jvmTest/kotlin/sample/SampleTestsJVM.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package sample
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlin.test.*
+
+class SampleTestsJVM {
+ @Test
+ fun testHello() {
+ assertTrue("JVM" in hello())
+ }
+
+ @Test
+ @OptIn(ExperimentalSerializationApi::class)
+ fun kindSimpleName() {
+ val kind = Int.serializer().descriptor.kind
+ val name = kind.toString()
+ assertEquals("INT", name)
+ }
+}
diff --git a/integration-test/src/macosMain/kotlin/sample/SampleMacos.kt b/integration-test/src/macosMain/kotlin/sample/SampleMacos.kt
new file mode 100644
index 00000000..af949d1e
--- /dev/null
+++ b/integration-test/src/macosMain/kotlin/sample/SampleMacos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample
+
+actual object Platform {
+ actual val name: String = "Native"
+}
diff --git a/integration-test/src/macosTest/kotlin/sample/SampleTestsNative.kt b/integration-test/src/macosTest/kotlin/sample/SampleTestsNative.kt
new file mode 100644
index 00000000..5ea67274
--- /dev/null
+++ b/integration-test/src/macosTest/kotlin/sample/SampleTestsNative.kt
@@ -0,0 +1,11 @@
+package sample
+
+import kotlin.test.Test
+import kotlin.test.assertTrue
+
+class SampleTestsNative {
+ @Test
+ fun testHello() {
+ assertTrue("Native" in hello())
+ }
+} \ No newline at end of file
diff --git a/knit.properties b/knit.properties
new file mode 100644
index 00000000..9cfed61a
--- /dev/null
+++ b/knit.properties
@@ -0,0 +1,5 @@
+knit.dir=guide/example/
+knit.package=example
+
+test.dir=guide/test/
+test.package=example.test
diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock
new file mode 100644
index 00000000..0ccaae25
--- /dev/null
+++ b/kotlin-js-store/yarn.lock
@@ -0,0 +1,560 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@ungap/promise-all-settled@1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
+ integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
+
+ansi-colors@4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
+ integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+anymatch@~3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
+ integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
+ dependencies:
+ normalize-path "^3.0.0"
+ picomatch "^2.0.4"
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+binary-extensions@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
+ integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+braces@~3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+ integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+ dependencies:
+ fill-range "^7.0.1"
+
+browser-stdout@1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
+ integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
+
+buffer-from@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+ integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+camelcase@^6.0.0:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e"
+ integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==
+
+chalk@^4.1.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+chokidar@3.5.2:
+ version "3.5.2"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
+ integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
+ dependencies:
+ anymatch "~3.1.2"
+ braces "~3.0.2"
+ glob-parent "~5.1.2"
+ is-binary-path "~2.1.0"
+ is-glob "~4.0.1"
+ normalize-path "~3.0.0"
+ readdirp "~3.6.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+cliui@^7.0.2:
+ version "7.0.4"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
+ integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.0"
+ wrap-ansi "^7.0.0"
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+debug@4.3.2:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
+ integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
+ dependencies:
+ ms "2.1.2"
+
+decamelize@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
+ integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
+
+diff@5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
+ integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+escalade@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+ integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-string-regexp@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+fill-range@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+ integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+find-up@5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+ integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+ dependencies:
+ locate-path "^6.0.0"
+ path-exists "^4.0.0"
+
+flat@^5.0.2:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
+ integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
+
+format-util@1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271"
+ integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+fsevents@~2.3.2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+ integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
+get-caller-file@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+glob-parent@~5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+ dependencies:
+ is-glob "^4.0.1"
+
+glob@7.1.7:
+ version "7.1.7"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
+ integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+growl@1.10.5:
+ version "1.10.5"
+ resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
+ integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+he@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+ integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+is-binary-path@~2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
+ integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+ dependencies:
+ binary-extensions "^2.0.0"
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-fullwidth-code-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-glob@^4.0.1, is-glob@~4.0.1:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-number@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-plain-obj@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
+ integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
+
+is-unicode-supported@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
+ integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+js-yaml@4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
+locate-path@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+ integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+ dependencies:
+ p-locate "^5.0.0"
+
+log-symbols@4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
+ integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
+ dependencies:
+ chalk "^4.1.0"
+ is-unicode-supported "^0.1.0"
+
+minimatch@3.0.4, minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+mocha@9.1.2:
+ version "9.1.2"
+ resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.2.tgz#93f53175b0f0dc4014bd2d612218fccfcf3534d3"
+ integrity sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w==
+ dependencies:
+ "@ungap/promise-all-settled" "1.1.2"
+ ansi-colors "4.1.1"
+ browser-stdout "1.3.1"
+ chokidar "3.5.2"
+ debug "4.3.2"
+ diff "5.0.0"
+ escape-string-regexp "4.0.0"
+ find-up "5.0.0"
+ glob "7.1.7"
+ growl "1.10.5"
+ he "1.2.0"
+ js-yaml "4.1.0"
+ log-symbols "4.1.0"
+ minimatch "3.0.4"
+ ms "2.1.3"
+ nanoid "3.1.25"
+ serialize-javascript "6.0.0"
+ strip-json-comments "3.1.1"
+ supports-color "8.1.1"
+ which "2.0.2"
+ workerpool "6.1.5"
+ yargs "16.2.0"
+ yargs-parser "20.2.4"
+ yargs-unparser "2.0.0"
+
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+ms@2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+nanoid@3.1.25:
+ version "3.1.25"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
+ integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==
+
+normalize-path@^3.0.0, normalize-path@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+ integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+p-limit@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+ integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+ dependencies:
+ yocto-queue "^0.1.0"
+
+p-locate@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+ integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+ dependencies:
+ p-limit "^3.0.2"
+
+path-exists@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+picomatch@^2.0.4, picomatch@^2.2.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+randombytes@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+ integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+ dependencies:
+ safe-buffer "^5.1.0"
+
+readdirp@~3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
+ integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
+ dependencies:
+ picomatch "^2.2.1"
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+
+safe-buffer@^5.1.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+serialize-javascript@6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
+ integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
+ dependencies:
+ randombytes "^2.1.0"
+
+source-map-support@0.5.20:
+ version "0.5.20"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9"
+ integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map@^0.6.0:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+string-width@^4.1.0, string-width@^4.2.0:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-json-comments@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+supports-color@8.1.1:
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+to-regex-range@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ dependencies:
+ is-number "^7.0.0"
+
+which@2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+workerpool@6.1.5:
+ version "6.1.5"
+ resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581"
+ integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==
+
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+y18n@^5.0.5:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+ integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
+yargs-parser@20.2.4:
+ version "20.2.4"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
+ integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
+
+yargs-parser@^20.2.2:
+ version "20.2.9"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
+ integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
+
+yargs-unparser@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
+ integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
+ dependencies:
+ camelcase "^6.0.0"
+ decamelize "^4.0.0"
+ flat "^5.0.2"
+ is-plain-obj "^2.1.0"
+
+yargs@16.2.0:
+ version "16.2.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
+ integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
+ dependencies:
+ cliui "^7.0.2"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
+ require-directory "^2.1.1"
+ string-width "^4.2.0"
+ y18n "^5.0.5"
+ yargs-parser "^20.2.2"
+
+yocto-queue@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+ integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
diff --git a/license/LICENSE.txt b/license/LICENSE.txt
new file mode 100644
index 00000000..f27c9881
--- /dev/null
+++ b/license/LICENSE.txt
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
diff --git a/license/NOTICE.txt b/license/NOTICE.txt
new file mode 100644
index 00000000..ee516ec0
--- /dev/null
+++ b/license/NOTICE.txt
@@ -0,0 +1,8 @@
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the kotlix.serialization library. ==
+ =========================================================================
+
+ kotlinx.serialization library.
+ Copyright 2017-2019 JetBrains s.r.o and respective authors and developers
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 00000000..10251efc
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+rootProject.name = 'kotlinx-serialization'
+
+include ':kotlinx-serialization-core'
+project(':kotlinx-serialization-core').projectDir = file('./core')
+
+include ':kotlinx-serialization-bom'
+project(':kotlinx-serialization-bom').projectDir = file('./bom')
+
+include ':kotlinx-serialization-json'
+project(':kotlinx-serialization-json').projectDir = file('./formats/json')
+
+include ':kotlinx-serialization-protobuf'
+project(':kotlinx-serialization-protobuf').projectDir = file('./formats/protobuf')
+
+include ':kotlinx-serialization-cbor'
+project(':kotlinx-serialization-cbor').projectDir = file('./formats/cbor')
+
+include ':kotlinx-serialization-hocon'
+project(':kotlinx-serialization-hocon').projectDir = file('./formats/hocon')
+
+include ':kotlinx-serialization-properties'
+project(':kotlinx-serialization-properties').projectDir = file('./formats/properties')
+
+include ':benchmark'
+project(':benchmark').projectDir = file('./benchmark')
+
+include ':guide'
+project(':guide').projectDir = file('./guide')
diff --git a/update_docs.sh b/update_docs.sh
new file mode 100755
index 00000000..86598287
--- /dev/null
+++ b/update_docs.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+
+# Abort on first error
+set -e
+
+# Directories
+ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+DIST_DIR="$ROOT_DIR/build/dokka/htmlMultiModule"
+PAGES_DIR="$ROOT_DIR/build/pages"
+
+# Init options
+GRADLE_OPT=
+PUSH_OPT=
+
+# Set dry run if needed
+if [ "$2" == "push" ] ; then
+ echo "--- Doing LIVE site deployment, so do clean build"
+ GRADLE_OPT=clean
+else
+ echo "--- Doing dry-run. To commit do 'update_docs.sh <version> push'"
+ PUSH_OPT=--dry-run
+fi
+
+# Makes sure that site is built
+"$ROOT_DIR/gradlew" $GRADLE_OPT dokkaHtmlMultiModule
+
+# Cleanup dist directory (and ignore errors)
+rm -rf "$PAGES_DIR" || true
+
+# Prune worktrees to avoid errors from previous attempts
+git --work-tree "$ROOT_DIR" worktree prune
+
+# Create git worktree for gh-pages branch
+git --work-tree "$ROOT_DIR" worktree add -B gh-pages "$PAGES_DIR" origin/gh-pages
+
+# Now work in newly created workspace
+cd "$PAGES_DIR"
+
+# Fixup all the old documentation files
+# Remove non-.html files
+REMOVE_FILES=$(find . -type f -not -name '.git')
+if [ "$REMOVE_FILES" != "" ] ; then
+ git rm $REMOVE_FILES > /dev/null
+fi
+
+# Copy manually new documentation and flat out kotlinx-serialization
+cp -r "$DIST_DIR"/* "$PAGES_DIR"
+
+# Add it all to git
+# git add *
+for file in $(find $PAGES_DIR -type f -name '*'); do git add $file; done
+
+
+# Commit docs for the new version
+if [ -z "$1" ] ; then
+ echo "No argument with version supplied -- skipping commit"
+else
+ git commit -m "Version $1 docs"
+ git push $PUSH_OPT origin gh-pages:gh-pages
+fi